美文网首页
FragmentPagerAdapter与FragmentSta

FragmentPagerAdapter与FragmentSta

作者: MDZZ灭顶之灾12138 | 来源:发表于2017-01-04 09:00 被阅读115次
    • android.support.v4.app.FragmentPagerAdapter
      继承自android.support.v4.view.PagerAdapter,每页都是一个Fragment,并且所有的Fragment实例一直保存在Fragment manager中。所以它适用于少量固定的fragment,比如一组用于分页显示的标签。除了当Fragment不可见时,它的视图层(view hierarchy)有可能被销毁外,每页的Fragment都会被保存在内存中.

    • android.support.v4.app.FragmentStatePagerAdapter
      继承自android.support.v4.view.PagerAdapter,每页都是一个Fragment,当Fragment不被需要时(比如不可见),整个Fragment都会被销毁,除了saved state被保存外(保存下来的bundle用于恢复Fragment实例)。所以它适用于很多页的情况。

    最开始处于第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:上上页的实例还保留着,只是销毁了它的视图:

    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(ViewGroupcontainer,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()```

    二者使用方法基本相同,唯一的区别就在卸载不再需要的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永远不会被销毁。

    • 更新ViewPager中的Fragment
      调用notifyDataSetChanged()
      时,2个adapter的方法的调用情况相同,当前页和相邻的两页的getItemPosition都会被调用到
    public int getItemPosition(Object object) { 
         return POSITION_UNCHANGED;
    }```
    
    只需要为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)```
    
    - 一个误区
    ViewPager.getChildCount()
     返回的是当前ViewPager所管理的没有被销毁视图的Fragment,并不是所有的Fragment。想要获取所有的Fragment数量,应该调用ViewPager.getAdapter().getCount()
    

    相关文章

      网友评论

          本文标题:FragmentPagerAdapter与FragmentSta

          本文链接:https://www.haomeiwen.com/subject/vmqiuttx.html