Android viewpager 动态刷新 及不一样的PagerAdapter

参考连接 (一)  https://blog.csdn.net/wbwjx/article/details/52939095java

FragmentPagerAdapter && FragmentStatePagerAdapter
FragmentPagerAdapter: 全部的Fragment实例一直保存在Fragment manager中,适用于少许固定的fragment,当Fragment不可见时,它的视图会被销毁.
当adapter须要一个指定位置的Fragment时,而且这个Fragment不存在时,getItem就被调到.
FragmentStatePagerAdapter: 当Fragment不可见时,整个Fragment实例都会被销毁,saveState()方法会被调用(用于下次恢复Fragment实例)
使用FragmentPagerAdapter时,当每页的Fragment被建立后,getItem()不会被调到了
对于FragmentStatePagerAdapter,因为Fragment被销毁,因此每次都会调用getItem()来判断是否要从新加载.android

 

ViewPager动态刷新办法
最终的解决办法,为了保证Fragment重建,和getItem会被调用,采用FragmentStatePagerAdapter,对于getItemPosition方法, 
须要保证已存在的Fragment复用,不存在的刷新重建.可以下实现.编程

public int getItemPosition(Object item) {
        MyFragment fragment = (MyFragment)item;
        String title = fragment.getTitle();
        int position = titles.indexOf(title);segmentfault

        if (position >= 0) {
            return position;
        } else {
            return POSITION_NONE;
        }
    }
//////////////////////////////数组

如何更新及替换ViewPager中的Fragment     

参考连接(二)    https://segmentfault.com/a/1190000003742057

 22.1k 次阅读  ·  读完须要 50 分钟app

10ide

How to update and replace fragment in viewpager?函数

ListView的工做原理

在了解ViewPager的工做原理以前,先回顾ListView的工做原理:this

  1. ListView只有在须要显示某些列表项时,它才会去申请可用的视图对象;若是为全部的列表项数据建立视图对象,会浪费内存;google

  2. ListView找谁去申请视图对象呢? 答案是adapter。adapter是一个控制器对象,负责从模型层获取数据,建立并填充必要的视图对象,将准备好的视图对象返回给ListView

  3. 首先,经过调用adapter的getCount()方法,ListView询问数组列表中包含多少个对象(为避免出现数组越界的错误);紧接着ListView就调用adapter的getView(int, View, ViewGroup)方法。

ViewPager某种程度上相似于ListView,区别在于:ListView经过ArrayAdapter.getView(int position, View convertView, ViewGroup parent)填充视图;ViewPager经过FragmentPagerAdapter.getItem(int position)生成指定位置的fragment.

而咱们须要关注的是:

ViewPager和它的adapter是如何配合工做的?

声明:本文内容针对android.support.v4.app.*
ViewPager有两个adapter:FragmentPagerAdapter和FragmentStatePagerAdapter:

继承自android.support.v4.view.PagerAdapter,每页都是一个Fragment,而且全部的Fragment实例一直保存在Fragment manager中。因此它适用于少许固定的fragment,好比一组用于分页显示的标签。除了当Fragment不可见时,它的视图层(view hierarchy)有可能被销毁外,每页的Fragment都会被保存在内存中。(翻译自代码文件的注释部分)

继承自android.support.v4.view.PagerAdapter,每页都是一个Fragment,当Fragment不被须要时(好比不可见),整个Fragment都会被销毁,除了saved state被保存外(保存下来的bundle用于恢复Fragment实例)。因此它适用于不少页的状况。(翻译自代码文件的注释部分)

它俩的子类,须要实现getItem(int) 和 android.support.v4.view.PagerAdapter.getCount().

先经过一段代码了解ViewPager和FragmentPagerAdapter的典型用法

稍后作详细分析:

// Set a PagerAdapter to supply views for this pager.
  ViewPager viewPager = (ViewPager) findViewById(R.id.my_viewpager_id);
  viewPager.setAdapter(mMyFragmentPagerAdapter);
 
  private FragmentPagerAdapter mMyFragmentPagerAdapter = new FragmentPagerAdapter(getSupportFragmentManager()) {
    @Override
    public int getCount() {
      return 2; // Return the number of views available.
    }
 
    @Override
    public Fragment getItem(int position) {
      return new MyFragment(); // Return the Fragment associated with a specified position.
    }
 
    // Called when the host view is attempting to determine if an item's position has changed.
    @Override
    public int getItemPosition(Object object) {
      if (object instanceof MyFragment) {
        ((MyFragment)object).updateView();
      }
      return super.getItemPosition(object);
    }
  };
 
  private class MyFragment extends Fragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      // do something such as init data
    }
 
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
      View view = inflater.inflate(R.layout.fragment_my, container, false);
      // init view in the fragment
      return view;
    }
 
    public void updateView() {
      // do something to update the fragment
    }
  }

FragmentPagerAdapter和FragmentStatePagerAdapter对Fragment的管理略有不一样,在详细考察两者区别以前,咱们经过两种较为直观的方式先感觉下:

经过两张图片直观的对比FragmentPagerAdapter和FragmentStatePagerAdapter的区别

说明:这两张图片来自于《Android权威编程指南》,原图有3个Fragment,我增长了1个Fragment,以及被调到的方法。
FragmentPagerAdapter的Fragment管理:
image-11-4-FragmentPagerAdapter的fragment管理-方法调用

FragmentStatePageAdapter的Fragment管理:
image-11-3FragmentStatePagerAdapter的fragment管理-方法调用

详细分析 adapter method和fragment lifecycle method 的调用状况

好啦,感觉完毕,咱们须要探究其详情,梳理adapter建立、销毁Fragment的过程,过程当中adapter method和fragment lifecycle method哪些被调到,有哪些同样,有哪些不同。

最开始处于第0页时,adapter不只为第0页建立Fragment实例,还为相邻的第1页建立了Fragment实例:

// 刚开始处在page0
D/Adapter (25946): getItem(0)
D/Fragment0(25946): newInstance(2015-09-10)  // 注释:newInstance()调用了Fragment的构造器方法,下同。
D/Adapter (25946): getItem(1)
D/Fragment1(25946): newInstance(Hello World, I'm li2.)
D/Fragment0(25946): onAttach()
D/Fragment0(25946): onCreate()
D/Fragment0(25946): onCreateView()
D/Fragment1(25946): onAttach()
D/Fragment1(25946): onCreate()
D/Fragment1(25946): onCreateView()

第1次从第0页滑到第1页,adapter一样会为相邻的第2页建立Fragment实例;

// 第1次滑到page1
D/Adapter (25946): onPageSelected(1)
D/Adapter (25946): getItem(2)
D/Fragment2(25946): newInstance(true)
D/Fragment2(25946): onAttach()
D/Fragment2(25946): onCreate()
D/Fragment2(25946): onCreateView()

FragmentPagerAdapter和FragmentStatePagerAdapter齐声说:呐,请主公贰放心,属下定会为您准备好相邻的下一页视图哒!么么哒!

它俩对待下一页的态度是相同的,但对于上上页,它俩作出了不同的事情:

FragmentPagerAdapter说:上上页的实例还保留着,只是销毁了它的视图

// 第N次(N不等于1)向右滑动选中page2
D/Adapter (25946): onPageSelected(2)
D/Adapter (25946): destroyItem(0)  // 销毁page0的视图
D/Fragment0(25946): onDestroyView()
D/Fragment3(25946): onCreateView()  // page3的Fragment实例仍保存在FragmentManager中,因此只需建立它的视图

FragmentStatePagerAdapter说:上上页的实例和视图都被俺销毁啦

// 第N次(N不等于1)向右滑选中page2
D/Adapter (27880): onPageSelected(2)
D/Adapter (27880): destroyItem(0)  // 销毁page0的实例和视图
D/Adapter (27880): getItem(3)  // 建立page3的Fragment
D/Fragment3(27880): newInstance()
D/Fragment0(27880): onDestroyView()
D/Fragment0(27880): onDestroy()
D/Fragment0(27880): onDetach()
D/Fragment3(27880): onAttach()
D/Fragment3(27880): onCreate()
D/Fragment3(27880): onCreateView()

Fragment getItem(int position)

// Return the Fragment associated with a specified position.
public abstract Fragment getItem(int position);

当adapter须要一个指定位置的Fragment,而且这个Fragment不存在时,getItem就被调到,返回一个Fragment实例给adapter。
因此,有必要再次强调,getItem是建立一个新的Fragment,可是这个方法名可能会被误认为是返回一个已经存在的Fragment
对于FragmentPagerAdapter,当每页的Fragment被建立后,这个函数就不会被调到了。对于FragmentStatePagerAdapter,因为Fragment会被销毁,因此它仍会被调到。
因为咱们必须在getItem中实例化一个Fragment,因此当getItem()被调用后,Fragment相应的生命周期函数也就被调到了:

D/Adapter (25946): getItem(1)
D/Fragment1(25946): newInstance(Hello World, I'm li2.)  // newInstance()调用了Fragment的构造器方法;
D/Fragment1(25946): onAttach()
D/Fragment1(25946): onCreate()
D/Fragment1(25946): onCreateView()

void destroyItem(ViewGroup container, int position, Object object)

// Remove a page for the given position. 
public void FragmentPagerAdapter.destroyItem(ViewGroup container, int position, Object object) {
    mCurTransaction.detach((Fragment)object);
}

public void FragmentStatePagerAdapter.destroyItem(ViewGroup container, int position, Object object) {
    mSavedState.set(position, mFragmentManager.saveFragmentInstanceState(fragment));
    mFragments.set(position, null);
    mCurTransaction.remove(fragment);
}

销毁指定位置的Fragment。从源码中能够看出两者的区别,一个detach,一个remove,这将调用到不一样的Fragment生命周期函数:

// 对于FragmentPagerAdapter
D/Adapter (25946): onPageSelected(2)
D/Adapter (25946): destroyItem(0)
D/Fragment0(25946): onDestroyView()  // 销毁视图

// 对于FragmentStatePagerAdapter
D/Adapter (27880): onPageSelected(2)
D/Adapter (27880): destroyItem(0)
D/Fragment0(27880): onDestroyView()  // 销毁视图
D/Fragment0(27880): onDestroy()  // 销毁实例
D/Fragment0(27880): onDetach()

FragmentPagerAdapter和FragmentStatePagerAdapter对比总结

两者使用方法基本相同,惟一的区别就在卸载再也不须要的fragment时,采用的处理方式不一样:

  • 使用FragmentStatePagerAdapter会销毁掉不须要的fragment。事务提交后,可将fragment从activity的FragmentManager中完全移除。类名中的“state”代表:在销毁fragment时,它会将其onSaveInstanceState(Bundle) 方法中的Bundle信息保存下来。用户切换回原来的页面后,保存的实例状态可用于恢复生成新的fragment.

  • FragmentPagerAdapter的作法大不相同。对于再也不须要的fragment,FragmentPagerAdapter则选择调用事务的detach(Fragment) 方法,而非remove(Fragment)方法来处理它。也就是说,FragmentPagerAdapter只是销毁了fragment的视图,但仍将fragment实例保留在FragmentManager中。所以, FragmentPagerAdapter建立的fragment永远不会被销毁。

(摘抄自《Android权威编程指南11.1.4》)

更新ViewPager中的Fragment

调用notifyDataSetChanged()时,2个adapter的方法的调用状况相同,当前页和相邻的两页的getItemPosition都会被调用到

// Called when the host view is attempting to determine if an item's position has changed. Returns POSITION_UNCHANGED if the position of the given item has not changed or POSITION_NONE if the item is no longer present in the adapter.
public int getItemPosition(Object object) {
    return POSITION_UNCHANGED;
}

从网上找到的解决办法是,覆写getItemPosition使其返POSITION_NONE,以触发Fragment的销毁和重建。但是这将致使Fragment频繁的销毁和重建,并非最佳的方法。
后来我把注意力放在了入口参数object上,"representing an item", 实际上就是Fragment,只须要为Fragment提供一个更新view的public方法:

@Override
// To update fragment in ViewPager, we should override getItemPosition() method,
// in this method, we call the fragment's public updating method.
public int getItemPosition(Object object) {
    Log.d(TAG, "getItemPosition(" + object.getClass().getSimpleName() + ")");
    if (object instanceof Page0Fragment) {
        ((Page0Fragment) object).updateDate(mDate);
    } else if (object instanceof Page1Fragment) {
        ((Page1Fragment) object).updateContent(mContent);
    } else if (object instanceof Page2Fragment) {
        ((Page2Fragment) object).updateCheckedStatus(mChecked);
    } else if (...) {
    }
    return super.getItemPosition(object);
};

// 更新界面时方法的调用状况
// 当前页为0时
D/Adapter (21517): notifyDataSetChanged(+0)
D/Adapter (21517): getItemPosition(Page0Fragment)
D/Fragment0(21517): updateDate(2015-09-12)
D/Adapter (21517): getItemPosition(Page1Fragment)
D/Fragment1(21517): updateContent(Hello World, I am li2.)

// 当前页为1时
D/Adapter (21517): notifyDataSetChanged(+1)
D/Adapter (21517): getItemPosition(Page0Fragment)
D/Fragment0(21517): updateDate(2015-09-13)
D/Adapter (21517): getItemPosition(Page1Fragment)
D/Fragment1(21517): updateContent(Hello World, I am li2.)
D/Adapter (21517): getItemPosition(Page2Fragment)
D/Fragment2(21517): updateCheckedStatus(true)
相关文章
相关标签/搜索