[android] ViewPager PagerAdapter가 View를 업데이트하지 않음
호환성 라이브러리에서 ViewPager를 사용하고 있습니다. 페이지를 통과 할 수있는 여러보기를 표시하는 데 성공했습니다.
그러나 새로운 View 집합으로 ViewPager를 업데이트하는 방법을 알아내는 데 어려움을 겪고 있습니다.
나는 호출과 같은 모든 종류의 것들을 시도했습니다 mAdapter.notifyDataSetChanged()
, mViewPager.invalidate()
심지어 새로운 어댑터 나 데이터의 새로운 목록을 사용할 때마다 생성.
아무것도 도움이되지 않았습니다. 텍스트보기는 원본 데이터와 변경되지 않습니다.
업데이트 :
약간의 테스트 프로젝트를 만들었고 뷰를 거의 업데이트 할 수있었습니다. 아래 수업을 붙여 넣을 게요.
그러나 업데이트되지 않는 것은 두 번째보기이며 ‘B’는 남아 있으며 업데이트 버튼을 누른 후 ‘Y’가 표시되어야합니다.
public class ViewPagerBugActivity extends Activity {
private ViewPager myViewPager;
private List<String> data;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
data = new ArrayList<String>();
data.add("A");
data.add("B");
data.add("C");
myViewPager = (ViewPager) findViewById(R.id.my_view_pager);
myViewPager.setAdapter(new MyViewPagerAdapter(this, data));
Button updateButton = (Button) findViewById(R.id.update_button);
updateButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
updateViewPager();
}
});
}
private void updateViewPager() {
data.clear();
data.add("X");
data.add("Y");
data.add("Z");
myViewPager.getAdapter().notifyDataSetChanged();
}
private class MyViewPagerAdapter extends PagerAdapter {
private List<String> data;
private Context ctx;
public MyViewPagerAdapter(Context ctx, List<String> data) {
this.ctx = ctx;
this.data = data;
}
@Override
public int getCount() {
return data.size();
}
@Override
public Object instantiateItem(View collection, int position) {
TextView view = new TextView(ctx);
view.setText(data.get(position));
((ViewPager)collection).addView(view);
return view;
}
@Override
public void destroyItem(View collection, int position, Object view) {
((ViewPager) collection).removeView((View) view);
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Parcelable saveState() {
return null;
}
@Override
public void restoreState(Parcelable arg0, ClassLoader arg1) {
}
@Override
public void startUpdate(View arg0) {
}
@Override
public void finishUpdate(View arg0) {
}
}
}
답변
이를 달성하는 몇 가지 방법이 있습니다.
첫 번째 옵션은 더 쉽지만 조금 더 비효율적입니다.
getItemPosition
다음 PagerAdapter
과 같이 재정의 하십시오 .
public int getItemPosition(Object object) {
return POSITION_NONE;
}
이 방법 notifyDataSetChanged()
으로을 호출 하면 뷰 호출기가 모든 뷰를 제거하고 다시로드합니다. 따라서 재 장전 효과가 얻어진다.
Alvaro Luis Bustamante (이전의 alvarolb) 가 제안한 두 번째 옵션 은 새보기를 인스턴스화 할 때 사용 하는 setTag()
방법입니다 instantiateItem()
. 그런 다음을 사용하는 대신 업데이트 할보기를 찾는 데 notifyDataSetChanged()
사용할 수 있습니다 findViewWithTag()
.
두 번째 방법은 매우 유연하고 성능이 뛰어납니다. 독창적 인 연구를 위해 알바 롤브에게 도움이됩니다.
답변
에 버그가 있다고 생각하지 않습니다 PagerAdapter
. 문제는 작동 방식을 이해하는 것이 약간 복잡하다는 것입니다. 여기에 설명 된 솔루션을 보면 오해가있어서 내 관점에서 인스턴스화 된 뷰를 잘못 사용합니다.
지난 몇 일 내가 함께 일하고있다 PagerAdapter
그리고 ViewPager
, 나는 다음을 발견 :
의 notifyDataSetChanged()
방법 은 기본 페이지가 변경되었음을 PagerAdapter
알려줍니다 ViewPager
. 예를 들어 페이지를 동적으로 만들거나 삭제 한 경우 (목록에서 항목 추가 또는 제거) 해당 페이지를 처리 ViewPager
해야합니다. 이 경우 및 메소드를 ViewPager
사용하여 새보기를 삭제하거나 인스턴스화 해야하는지 여부를 결정 한다고 생각합니다 .getItemPosition()
getCount()
나는 전화를 ViewPager
한 후에 notifyDataSetChanged()
아이를보고 그들의 위치를 확인 한다고 생각 합니다 getItemPosition()
. 아동이 방법의 반환을 볼 경우 POSITION_NONE
는이 ViewPager
뷰가 호출, 삭제 된 것을 이해 destroyItem()
하고,이 뷰를 제거.
이런 식으로 페이지의 컨텐츠 만 업데이트하려는 경우 getItemPosition()
항상 리턴하도록 재정의 하는 것은 POSITION_NONE
완전히 잘못된 것입니다. 이전에 작성된보기는 소멸되고 호출 할 때마다 새보기가 작성되기 때문 notifyDatasetChanged()
입니다. 몇 TextView
초 동안 그렇게 잘못되지 는 않았지만 데이터베이스에서 채워진 ListViews와 같은 복잡한 뷰가있는 경우 실제 문제가 될 수 있으며 리소스 낭비가 될 수 있습니다.
따라서 뷰를 다시 제거하고 인스턴스화하지 않고도 뷰의 컨텐츠를 효율적으로 변경하는 몇 가지 방법이 있습니다. 해결하려는 문제에 따라 다릅니다. 내 접근법은 setTag()
메소드의 인스턴스화 된보기에 메소드 를 사용하는 것입니다 instantiateItem()
. 따라서 데이터를 변경하거나 필요한 뷰를 무효화하려는 경우의 findViewWithTag()
메소드를 호출 ViewPager
하여 이전에 인스턴스화 된 뷰를 검색하고 원하는 때마다 새 뷰를 삭제 / 생성 할 필요없이 원하는대로 수정 / 사용할 수 있습니다. 값을 업데이트합니다.
예를 들어 100 페이지에 100 페이지가 TextView
있고 주기적으로 하나의 값만 업데이트하려고 한다고 가정하십시오 . 앞에서 설명한 접근 방식을 사용하면 TextView
각 업데이트에서 100 초를 제거하고 인스턴스화하고 있음을 의미합니다 . 그것은 이해가되지 않습니다…
답변
변화 FragmentPagerAdapter
에를 FragmentStatePagerAdapter
.
getItemPosition()
메서드를 재정의하고을 반환 POSITION_NONE
합니다.
결국에는 notifyDataSetChanged()
온뷰 호출기를 듣게됩니다 .
답변
alvarolb 의 대답 은 확실히 최선의 방법입니다. 그의 대답을 바탕으로 이것을 구현하는 쉬운 방법은 위치별로 활성 뷰를 저장하는 것입니다.
SparseArray<View> views = new SparseArray<View>();
@Override
public Object instantiateItem(View container, int position) {
View root = <build your view here>;
((ViewPager) container).addView(root);
views.put(position, root);
return root;
}
@Override
public void destroyItem(View collection, int position, Object o) {
View view = (View)o;
((ViewPager) collection).removeView(view);
views.remove(position);
view = null;
}
그런 다음 notifyDataSetChanged
메소드를 재정 의하여 뷰를 새로 고칠 수 있습니다 …
@Override
public void notifyDataSetChanged() {
int key = 0;
for(int i = 0; i < views.size(); i++) {
key = views.keyAt(i);
View view = views.get(key);
<refresh view with new data>
}
super.notifyDataSetChanged();
}
당신은 실제로 비슷한 코드를 사용할 수 있습니다 instantiateItem
및 notifyDataSetChanged
보기를 새로 고침 할 수 있습니다. 내 코드에서는 정확히 같은 방법을 사용합니다.
답변
같은 문제가 있었다. 나를 위해 FragmentStatePagerAdapter를 확장하고 아래 메소드를 재정의했습니다.
@Override
public Parcelable saveState() {
return null;
}
@Override
public void restoreState(Parcelable state, ClassLoader loader) {
}
답변
좌절의 시간이 문제를 극복하기 위해 위의 모든 솔루션을 시도 또한 같은 다른 유사한 문제에 많은 솔루션을하는 동안 후 이 , 이 그리고 이 모두가이 문제를 해결하고 만들기 위해 나와 함께 실패하는 ViewPager
기존의 파괴 Fragment
와 채우기 pager
로 새로운 Fragment
s. 다음과 같이 문제를 해결했습니다.
1) 만들기 기능 ViewPager
확장에 클래스를 FragmentPagerAdapter
다음과 같은 :
public class myPagerAdapter extends FragmentPagerAdapter {
2) 에 대한 항목을 작성 ViewPager
가게가 title
와를 fragment
다음과 같은 :
public class PagerItem {
private String mTitle;
private Fragment mFragment;
public PagerItem(String mTitle, Fragment mFragment) {
this.mTitle = mTitle;
this.mFragment = mFragment;
}
public String getTitle() {
return mTitle;
}
public Fragment getFragment() {
return mFragment;
}
public void setTitle(String mTitle) {
this.mTitle = mTitle;
}
public void setFragment(Fragment mFragment) {
this.mFragment = mFragment;
}
}
3) 인스턴스 생성자 를 다음과 같이 ViewPager
내 FragmentManager
인스턴스에 저장하십시오 class
.
private FragmentManager mFragmentManager;
private ArrayList<PagerItem> mPagerItems;
public MyPagerAdapter(FragmentManager fragmentManager, ArrayList<PagerItem> pagerItems) {
super(fragmentManager);
mFragmentManager = fragmentManager;
mPagerItems = pagerItems;
}
4) 다음과 같이 새 목록에서 새 항목을 다시 설정 하기 위해 직접 adapter
이전 항목 fragment
을 모두 삭제하여 새 데이터로 데이터 를 재설정하는 방법을 작성하십시오 .fragmentManager
adapter
fragment
public void setPagerItems(ArrayList<PagerItem> pagerItems) {
if (mPagerItems != null)
for (int i = 0; i < mPagerItems.size(); i++) {
mFragmentManager.beginTransaction().remove(mPagerItems.get(i).getFragment()).commit();
}
mPagerItems = pagerItems;
}
5) 컨테이너에서 Activity
또는 Fragment
새 데이터로 어댑터를 다시 초기화하지 마십시오. setPagerItems
다음과 같이 새 데이터로 메소드 를 통해 새 데이터를 설정하십시오 .
ArrayList<PagerItem> pagerItems = new ArrayList<PagerItem>();
pagerItems.add(new PagerItem("Fragment1", new MyFragment1()));
pagerItems.add(new PagerItem("Fragment2", new MyFragment2()));
mPagerAdapter.setPagerItems(pagerItems);
mPagerAdapter.notifyDataSetChanged();
도움이 되길 바랍니다.
답변
나는 같은 문제가 있었고 내 솔루션은 다음 FragmentPagerAdapter
을 재정 의하여 사용 하고 있습니다 FragmentPagerAdapter#getItemId(int position)
.
@Override
public long getItemId(int position) {
return mPages.get(position).getId();
}
기본적으로이 메소드는 항목의 위치를 반환합니다. 변경 ViewPager
되었는지 확인하고 itemId
변경된 경우에만 페이지를 다시 작성 한다고 가정합니다 . 그러나 오버라이드되지 않은 버전은itemId
페이지가 실제로 다른 경우 하며 ViewPager는 해당 페이지가 교체 된 페이지를 정의하지 않으므로 다시 작성해야합니다.
이것을 사용하려면 long id
각 페이지마다 필요합니다. 일반적으로 고유 할 것으로 예상되지만이 경우 동일한 페이지의 이전 값과 달라야한다고 제안합니다. 따라서 여기서는 연속 카운터를 어댑터 또는 임의의 정수 (넓은 분포)로 사용할 수 있습니다.
이 주제에서 솔루션으로 언급 된 관점 태그를 사용하는 것이보다 일관된 방법이라고 생각합니다. 그러나 모든 경우에 해당되는 것은 아닙니다.