android-viewpager - ViewPager PagerAdapter不需要更新View

  显示原文与译文双语对照的内容

我正在使用兼容库中的ViewPager 。 我让succussfully显示了几个视图,我可以通过它来浏览。

但是,我想知道如何用一组新的视图来更新 ViewPager 。

我尝试了各种类似于调用 mAdapter.notifyDataSetChanged()mViewPager.invalidate(),甚至每次我想使用新的数据列表时都创建一个全新的适配器。

没有什么帮助,textviews保持原始数据不变。

感谢你们抽空到来

更新: 我做了一个小测试项目,几乎可以更新视图。 我将在下面粘贴类。

没有更新的是 2视图,'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) {
 }
 }
}

时间:

有几种方法可以实现这里目的。

第一个选项比较简单,但效率更低。

在你 PagerAdapter 像this,重写


public int getItemPosition(Object object) {
 return POSITION_NONE;
}

这样,当你调用 notifyDataSetChanged() 时,视图页导航将删除所有视图并重新加载所有视图。 因此,重新加载效果得到了。

第二个选项,建议的Alvaro Luis Bustamante ( 以前 alvarolb ),是在实例化新视图时在 instantiateItem() 中使用 setTag() 方法。 然后,而不是使用 notifyDataSetChanged(),你可以使用 findViewWithTag() 查找要更新的视图。

第二种方法非常灵活和高性能。 alvarolb对原始研究的赞誉。

我想应该没有任何类型的在 问题是理解它的工作方式有点复杂。 看看这里解释的解决方案,有一个误解,因此从我的观点来看,实例化的视图是一个糟糕的用法。

最近几天我一直在使用 PagerAdapterViewPager,我发现如下:

上的notifyDataSetChanged() 方法 PagerAdapter 将只与网站 ViewPager,它必须在页面有修改。 例如如果已经创建/删除的页面动态地在 ViewPager ( 从列表中添加或者删除项目) 应该考虑它。 在这种情况下,我认为 ViewPager 决定了是否应该使用 getItemPosition()getCount() 方法来删除或者实例化一个新视图。

随着我觉得 ViewPager,经过一 notifyDataSetChanged() 调用接受它是子视图并检查他们的姿势, 如果对于子视图,这里方法返回 POSITION_NONEViewPager 会理解该视图已经被删除,调用了 destroyItem() 并删除了该视图。

通过这种方式,重写 getItemPosition() 始终返回 POSITION_NONE 是完全错误的,因为你只想更新页面的内容,因为以前创建的视图将被破坏,并且每次调用 notifyDataSetChanged() 时会创建新的视图。 这个也许看来也不太错误的只进行少量 TextView s,但是当你有复杂的视图,如ListViews数据来自一个数据库,这可能成为一个真正的问题和一种资源浪费。

因此,有几种方法可以有效地更改视图内容,而不必再次删除和实例化视图。 这取决于你想解决的问题。 在 instantiateItem() method, setTag()我的方法是使用的方法对于任何实例化 view. 所以当你想更改数据或者使视图失效,你需要,可以调用 findViewWithTag()ViewPager 上的方法来检索所需的先前实例化查看和修改/使用它作为,而不必删除/创建新的视图每次要更新某个值。

假设你有 100个带有 100 TextView的页面,并且只想定期更新一个值。 通过前面解释的方法,这意味着你在每次更新时都要删除并实例化 100 TextView 。 这没有意义。。

答案 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();
}

你可以在 instantiateItemnotifyDataSetChanged 中使用类似的代码来刷新你的视图。 在我的代码中,我使用了完全相同的方法。

下班后的挫折,同时尝试所有上述解决方案来克服这个问题,同样也在许多解决方案对其他类似的问题,比如这个 ,这里这个哪个都没有成功,让我解决这个问题,以使 ViewPager 毁了那旧的与新的FragmentpagerFragment 和填充s 。 我已经解决了以下问题:

1 ) 使 ViewPager 类扩展了 FragmentPagerAdapter 为一行代码。


 public class myPagerAdapter extends FragmentPagerAdapter {

2 ) 创建了某项新的Fragmenttitle 并存储为 ViewPager,重新启动


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 实例来存储它,如下所示:


private FragmentManager mFragmentManager;
private ArrayList<PagerItem> mPagerItems;

public MyPagerAdapter(FragmentManager fragmentManager, ArrayList<PagerItem> pagerItems) {
 super(fragmentManager);
 mFragmentManager = fragmentManager;
 mPagerItems = pagerItems;
}

4 ) 创建一个方法,将用新的数据直接通过删除之前的所有从 FragmentManager 本身 Fragment re-set adapter的数据以使 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;
}

容器 Activity 或者 Fragment 中的5 不使用新数据re-initialize适配器。 通过方法 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();

希望有帮助。

经过大量的搜索这个问题,我找了一个非常好的解决方案,我认为是关于这事儿该走哪条路 基本上,只有当视图被实例化时,才会调用 instantiateItem,除非视图被破坏,否则它不会再次被调用。 相反,你所需要做的就是保存 所创建视图和更新它们在适配器中,生成一个获取函数以便其他用户可以更新它,还是涉及到的集函数更新( 我的收藏)的适配器。

在你的MyViewPagerAdapter中添加一个变量:


private View updatableView;

在你的instantiateItem中:


 public Object instantiateItem(View collection, int position) {
 updatableView = new TextView(ctx);//My change is here
 view.setText(data.get(position));
 ((ViewPager)collection).addView(view);
 return view;
 }

这样,你可以创建一个将更新你的视图的函数:


public updateText(String txt)
{
 ((TextView)updatableView).setText(txt);
}

希望有帮助!

我有一个类似的问题,我有四个页面,其中一个页面更新了其他三个页面的视图。 我能够更新与当前页面相邻的页面上的widgets(SeekBars, TextViews, etc.) 。 最后两个页面在调用 mTabsAdapter.getItem(position) 时没有初始化小部件。

来解决我的问题,我在调用 getItem(position)setSelectedPage(index) 之前使用。 这将实例化页面,允许我修改每个页面上的值和小部件。

在所有更新之后,我将使用 setSelectedPage(position) 后跟 notifyDataSetChanged()

在主更新页面的ListView中可以看到轻微的闪烁,但没有明显的变化。 我还没有彻底测试它,但它确实解决了我的即时问题。

如果有人使用基于 FragmentStatePagerAdapter的adapter(which will let ViewPager create minimum pages needed for display purpose, at most 2 for my case), @rui.araujo's 应答来覆盖适配器中的getItemPosition,将不会造成严重的浪费,但仍然可以改进。

在伪代码中:


public int getItemPosition(Object object) {
 YourFragment f = (YourFragment) object;
 YourData d = f.data;
 logger.info("validate item position on page index:" + d.pageNo);

 int dataObjIdx = this.dataPages.indexOf(d);

 if (dataObjIdx <0 || dataObjIdx!= d.pageNo) {
 logger.info("data changed, discard this fragment.");
 return POSITION_NONE;
 }

 return POSITION_UNCHANGED;
}

...