我正在使用兼容性库中的ViewPager。我已经成功地让它显示几个视图,我可以通过页面。
但是,我很难弄清楚如何用一组新的视图更新ViewPager。
我已经尝试了各种各样的事情,比如调用mAdapter.notifyDataSetChanged(), mviewpage .invalidate(),甚至在每次我想使用新的数据列表时创建一个全新的适配器。
没有任何帮助,textviews保持不变,从原始数据。
更新:
我做了一个小测试项目,我几乎能够更新视图。我将在下面粘贴这个类。
然而,似乎没有更新的是第二个视图,“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) {
}
}
}
我有同样的问题,我的解决方案是使用FragmentPagerAdapter覆盖FragmentPagerAdapter#getItemId(int位置):
@Override
public long getItemId(int position) {
return mPages.get(position).getId();
}
默认情况下,此方法返回项目的位置。我认为ViewPager检查itemId是否被更改,只有当它被更改时才重新创建页面。但是未覆盖的版本返回与itemId相同的位置,即使页面实际上不同,ViewPager没有定义页面被替换并需要重新创建。
要使用此功能,每个页面都需要长id。通常,它应该是唯一的,但我建议,对于这种情况,它应该不同于相同页面的前一个值。因此,在适配器或随机整数(具有广泛的分布)中使用连续计数器是可能的。
我认为这是更一致的方式,而不是使用标签的视图作为解决方案,在这个主题中提到。但可能不是所有情况都适用。
我把我自己的解决方案留在这里,这是一个变通办法,因为问题似乎是FragmentPagerAdapter不清理之前的片段,你可以添加到ViewPager,在片段管理器。所以,在添加FragmentPagerAdapter之前,我创建了一个方法来执行:
(在我的情况下,我从来没有添加超过3个片段,但你可以使用例如getFragmentManager(). getbackstackentrycount(),并检查所有的片段。
/**
* this method is solving a bug in FragmentPagerAdapter which don't delete in the fragment manager any previous fragments in a ViewPager.
*
* @param containerId
*/
public void cleanBackStack(long containerId) {
FragmentTransaction transaction = getFragmentManager().beginTransaction();
for (int i = 0; i < 3; ++i) {
String tag = "android:switcher:" + containerId + ":" + i;
Fragment f = getFragmentManager().findFragmentByTag(tag);
if (f != null) {
transaction.remove(f);
}
}
transaction.commit();
}
我知道这是一种变通方法,因为如果框架创建标签的方式发生变化,它将停止工作。
(当前为"android:switcher:" + containerId + ":" + i ")
那么使用方法是在得到容器之后:
ViewPager viewPager = (ViewPager) view.findViewById(R.id.view_pager);
cleanBackStack(viewPager.getId());
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中使用类似的代码来刷新视图。在我的代码中,我使用了完全相同的方法。
你只需要这段简单的代码:
代码:
private void updateAdapter() {
if (adapterViewPager != null) {
int from = vpMyViewPager.getCurrentItem() - vpMyViewPager.getOffscreenPageLimit();
int to = vpMyViewPager.getCurrentItem() + vpMyViewPager.getOffscreenPageLimit();
vpMyViewPager.removeAllViews();
for (int i = from; i <= to; i++) {
if (i < 0) {
continue;
}
adapterViewPager.instantiateItem(vpMyViewPager, i);
}
}
}
解释:
If you haven't changed offscreenPageLimit of the ViewPager, it always has 3 to 4 children depending on which direction you are going. And in order to show the correct content inside its children, it uses the adapter to get the right content.
now when you call removeAllViews() on your ViewPager, only 3 to 4 Views are actually being removed from the Window's hierarchy, and by calling instantiateItem(ViewGroup viewPager, int index), you are only recreating 3 to 4 Views. Then everything is back to normal, you swipe and scroll, the ViewPager shows contents using its adapter. The number of its children is not the same on all devices in all situations, for example if you set the offscreenPageLimit to 5, it will probably have around 11 to 12 children, but that's all, it's not much. it's fast.