转载自:http://www.cnblogs.com/dancefire/archive/2013/01/02/why-notifyDataSetChanged-does-not-work.htmlhtml
在一个 Android 应用中,我使用 FragmentPagerAdapter 来处理多 Fragment 页面的横向滑动。不过我碰到了一个问题,即当 Fragment 对应的数据集发生改变时,我但愿可以经过调用 mAdapter.notifyDataSetChanged() 来触发 Fragment 页面使用新的数据调整或从新生成其内容,但是当我调用 notifyDataSetChanged() 后,发现什么都没发生。java
搜索以后发现不止我一我的碰到这个问题,你们给出的解决办法五花八门,有些确实解决了问题,可是我总感受问题没搞清楚。因而我决定搞明白这个问题究竟是怎么回事,以及正确的用法到底如何。要搞明白这个问题,仅仅阅读文档并不足够,还须要阅读相关几个类的相关方法的实现,搞懂其设计意图。下面就是经过阅读源代码搞明白的内容。android
【ViewPager】app
ViewPager 如其名所述,是负责翻页的一个 View。准确说是一个 ViewGroup,包含多个 View 页,在手指横向滑动屏幕时,其负责对 View 进行切换。为了生成这些 View 页,须要提供一个 PagerAdapter 来进行和数据绑定以及生成最终的 View 页。ide
【PagerAdapter】函数
PageAdapter 是 ViewPager 的支持者,ViewPager 将调用它来取得所需显示的页,而 PageAdapter 也会在数据变化时,通知 ViewPager。这个类也是FragmentPagerAdapter 以及 FragmentStatePagerAdapter 的基类。若是继承自该类,至少须要实现 instantiateItem(), destroyItem(), getCount() 以及 isViewFromObject()。ui
【FragmentPagerAdapter】google
FragmentPagerAdapter 继承自 PagerAdapter。相比通用的 PagerAdapter,该类更专一于每一页均为 Fragment 的状况。如文档所述,该类内的每个生成的 Fragment 都将保存在内存之中,所以适用于那些相对静态的页,数量也比较少的那种;若是须要处理有不少页,而且数据动态性较大、占用内存较多的状况,应该使用FragmentStatePagerAdapter。FragmentPagerAdapter 重载实现了几个必须的函数,所以来自 PagerAdapter 的函数,咱们只须要实现 getCount(),便可。且,因为 FragmentPagerAdapter.instantiateItem() 的实现中,调用了一个新增的虚函数 getItem(),所以,咱们还至少须要实现一个 getItem()。所以,整体上来讲,相对于继承自 PagerAdapter,更方便一些。spa
【FragmentStatePagerAdapter】设计
FragmentStatePagerAdapter 和前面的 FragmentPagerAdapter 同样,是继承子 PagerAdapter。可是,和 FragmentPagerAdapter 不同的是,正如其类名中的 'State' 所代表的含义同样,该 PagerAdapter 的实现将只保留当前页面,当页面离开视线后,就会被消除,释放其资源;而在页面须要显示时,生成新的页面(就像 ListView 的实现同样)。这么实现的好处就是当拥有大量的页面时,没必要在内存中占用大量的内存。
讨论
以前看到一些解决办法,有的认为这是一个 bug,应该被修复;有的建议不用 FragmentPagerAdapter,而改用 FragmentStatePagerAdapter,而且重载 getItemPosition() 并返回 POSITION_NONE,以触发销毁对象以及重建对象。从上面的分析中看,后者给出的建议确实能够达到调用 notifyDataSetChanged() 后,Fragment 被以新的参数从新创建的效果。
可是问题在于,若是咱们只能这么解决这个问题,岂不是 FragmentPagerAdapter 就用不上了?最关键的是,两者对应的状况不一样。对于页面相对较少的状况,我仍旧但愿可以将生成的 Fragment 保存在内存中,在须要显示的时候直接调用,而不要产生生成、销毁对象的额外的开销,这样效率更高。这种状况下,选择 FragmentPagerAdapter 是更适合,不加考虑的选择 FragmentStatePagerAdapter 是不合适的。咱们不可以因噎废食。
所以,对于 FragmentPagerAdapter 的解决方案就是,分别重载 getItem() 以及 instantiateItem() 对象。getItem() 只用于生成新的与数据无关的 Fragment;而 instantiateItem() 函数则先调用父类中的 instantiateItem() 取得所对应的 Fragment 对象,而后,根据对应的数据,调用该对象对应的方法进行数据设置。
固然,不要忘记重载 getItemPosition() 函数,返回 POSITION_NONE,这个两个类的解决方案都须要的。两者不一样之处在于,FragmentStatePagerAdapter 在会在因 POSITION_NONE 触发调用的 destroyItem() 中真正的释放资源,从新创建一个新的 Fragment;而 FragmentPagerAdapter 仅仅会在 destroyItem() 中 detach 这个 Fragment,在 instantiateItem() 时会使用旧的 Fragment,并触发 attach,所以没有释放资源及重建的过程。
这样,当 notifyDataSetChanged() 被调用后,会最终触发 instantiateItem(),而无论 getItem() 是否被调用,咱们都在重载的 instantiateItem() 函数中已经将所须要的数据传递给了相应的 Fragment。在 Fragment 接下来的 onCreateView(), onStart() 以及 onResume() 的事件中,它能够正确的读取新的数据,Fragment 被成功复用了。
这里须要注意一个问题,在 Fragment 没有被添加到 FragmentManager 以前,咱们能够经过 Fragment.setArguments() 来设置参数,并在 Fragment 中,使用 getArguments() 来取得参数。这是经常使用的参数传递方式。可是这种方式对于咱们说的状况不适用。由于这种数据传递方式只可能用一次,在 Fragment 被添加到 FragmentManager 后,一旦被使用,咱们再次调用 setArguments() 将会致使 java.lang.IllegalStateException: Fragment already active 异常。所以,咱们这里的参数传递方式选择是,在继承的 Fragment 子类中,新增几个 setter,而后经过这些 setter 将数据传递过去。反向也是相似。相关信息能够参考 [5]。哦,这些 setter 中要注意不要操做那些 View,这些 View 只有在 onCreateView() 事件后才能够操做。
针对 FragmentPagerAdapter 的解决办法以下列代码所示:
1 @Override
2 public Fragment getItem(int position) {
3 MyFragment f = new MyFragment();
4 return f;
5 }
6
7 @Override
8 public Object instantiateItem(ViewGroup container, int position) {
9 MyFragment f = (MyFragment) super.instantiateItem(container, position);
10 String title = mList.get(position);
11 f.setTitle(title);
12 return f;
13 }
14
15 @Override
16 public int getItemPosition(Object object) {
17 return PagerAdapter.POSITION_NONE;
18 }
参考
Android 文档:
[1] http://developer.android.com/reference/android/support/v4/view/PagerAdapter.html
[2] http://developer.android.com/reference/android/support/v4/app/FragmentPagerAdapter.html
[3] http://developer.android.com/reference/android/support/v4/app/FragmentStatePagerAdapter.html
[4] http://developer.android.com/reference/android/support/v4/view/ViewPager.html
[5] http://developer.android.com/guide/components/fragments.html#CommunicatingWithActivity
Android 源代码:
Android Issue List:
[10] http://code.google.com/p/android/issues/detail?id=19001