如何高效的使用ViewPager,以及FragmentPagerAdapter與FragmentStatePagerAdapter的區別
ViewPager介紹
Layout manager that allows the user to flip left and right through pages of data. You supply an implementation of a PagerAdapter to generate the pages that the view shows.
ViewPager is most often used in conjunction with Fragment , which is a convenient way to supply and manage the lifecycle of each page. There are standard adapters implemented for using fragments with the ViewPager, which cover the most common use cases. These are FragmentPagerAdapter and FragmentStatePagerAdapter ; each of these classes have simple code showing how to build a full user interface with them.
通過官網的介紹,我們可以看出,Viewpager可以通過PagerAdapter展示數據,同時ViewPager更常用于和Fragment結合使用,這樣可以更好的管理頁面的生命周期。有兩種標準的適配器(FragmentPagerAdapter和FragmentStatePagerAdapter)來使fragments和ViewPager更好的結合,并且它們也滿足大多數的情況了。
PagerAdapter
Base class providing the adapter to populate pages inside of a ViewPager .
You will most likely want to use a more specific implementation of this, such as FragmentPagerAdapter or FragmentStatePagerAdapter
PagerAdapter是ViewPager適配器的基類。也許你更想要一個具體的實現,比如FragmentPagerAdapter和FragmentStatePagerAdapter
一般我們在只用簡單View來適配ViewPager的時候會選擇使用PagerAdapter,比如輪播圖。
When you implement a PagerAdapter, you must override the following methods at minimum:
- instantiateItem(ViewGroup, int)
- destroyItem(ViewGroup, int, Object);
- getCount();
- isViewFromObject(View, Object);
FragmentPagerAdapter 和 FragmentStatePagerAdapter的區別
在使用Fragment來適配ViewPager的時候,我們可以選擇FragmentPagerAdapter 或者FragmentStatePagerAdapter作為適配器。但是這兩者又有什么區別呢? 我們可以通過官網對這兩者的介紹來做一個大體的了解,然后通過具體的實踐來進行更深入的使用。
FragmentPagerAdapter
Implementation of PagerAdapter that represents each page as a Fragment that is persistently kept in the fragment manager as long as the user can return to the page.
This version of the pager is best for use when there are a handful of typically more static fragments to be paged through, such as a set of tabs. The fragment of each page the user visits will be kept in memory, though its view hierarchy may be destroyed when not visible. This can result in using a significant amount of memory since fragment instances can hold on to an arbitrary amount of state. For larger sets of pages, consider FragmentStatePagerAdapter
FragmentPagerAdapter繼承自 PagerAdapter。相比通用的 PagerAdapter,該類更專注于每一頁均為 Fragment 的情況。如文檔所述, 該類內的每一個生成的 Fragment 都將保存在內存之中 ,因此適用于那些相對靜態的頁,數量也比較少的那種;如果需要處理有很多頁,并且數據動態性較大、占用內存較多的情況,應該使用FragmentStatePagerAdapter
FragmentStatePagerAdapter
Implementation of PagerAdapter that uses a Fragment to manage each page. This class also handles saving and restoring of fragment's state.
This version of the pager is more useful when there are a large number of pages, working more like a list view. When pages are not visible to the user, their entire fragment may be destroyed, only keeping the saved state of that fragment. This allows the pager to hold on to much less memory associated with each visited page as compared to FragmentPagerAdapter at the cost of potentially more overhead when switching between pages.
FragmentStatePagerAdapter和前面的 FragmentPagerAdapter 一樣,是繼承子 PagerAdapter。但是,和 FragmentPagerAdapter 不一樣的是,正如其類名中的 'State' 所表明的含義一樣,該 PagerAdapter 的實現將只保留當前頁面,當頁面離開視線后,就會被 消除 ,釋放其資源;而在頁面需要顯示時,生成新的頁面(就像 ListView 的實現一樣)。這么實現的好處就是當擁有大量的頁面時,不必在內存中占用大量的內存。
通過觀察Fragment的生命周期來看其中的不同
在觀察ViewPager中Fragment的生命周期之前,我們先來看一個ViewPager的方法:
/**
- Set the number of pages that should be retained to either side of the
- current page in the view hierarchy in an idle state. Pages beyond this
- limit will be recreated from the adapter when needed.
*/
viewPager.setOffscreenPageLimit(int limit);</code></pre>
該方法是設置左右被保留的page的數量的,也就是說當前被保留的page數量為 2*limit + 1。至于那些沒有被保留的,又處于什么狀態了呢?這就需要觀察Fragment的生命周期了。
FragmentPagerAdapter
此處我們設置viewPager.setOffscreenPageLimit(1);

FragmentPagerAdapter初始化.png
通過Log打印我們可以看出,在 剛進入 ViewPager的時候,默認的應該是第0個page,
此時第0個page的生命周期是: onAttach -> onCreate -> onCreateView -> onViewCreated -> onActivityCreated -> onStart -> onResume 。同時 setUserVisibleHint:true
此時第1個page的生命周期是: onAttach -> onCreate -> onCreateView -> onViewCreated -> onActivityCreated -> onStart -> onResume 同時 setUserVisibleHint:false
可以看出,在初始化的過程,兩個Fragment的生命周期是一致,而且第1個page此時雖然不可以見,但是依然調用了onStart,onResume。我們都知道在Activity的生命周期中,只有當Acitivty是可見的時候才會調用onStart,只有在可以交互的時候才會調用onResume。
此時我們向右滑動,讓 第1個 page顯示在界面上。

currentPage==1.png
此時第0個page的生命周期是沒有發生變化的,因為我們設置了setOffscreenPageLimit(1);只是 javasetUserVisibleHint:false
第1個page有了一點變化: setUserVisibleHint:true
此時第2個page的生命周期有了變化: onAttach -> onCreate -> onCreateView -> onViewCreated -> onActivityCreated -> onStart -> onResume
此時我們向右滑動,讓 第2個 page顯示在界面上。

currentPage==2
此時第0個page的生命周期變化為: onPause -> onStop -> onDestroyView
第1個page生命周期沒有變化,只是 setUserVisibleHint:false
第2個page生命周期沒有發生變化,只是 setUserVisibleHint:true
第三個生命周期 onAttach -> onCreate -> onCreateView -> onViewCreated -> onActivityCreated -> onStart -> onResume 同時 javasetUserVisibleHint:false
可以看出,此時第0個page已經執行了 onDestroyView 了,但是它卻沒有走 onDestroy() 和 onDetach() 說明該page只是將View進行了回收,而真正的fragment是還沒有被回收的
此時我們向左滑動,讓 第1個 page顯示在界面上。

currentPage == 1
可以看出,page0只是走了 onCreateView -> onViewCreated ... 再次證明fragment沒有被真正的回收
FragmentStatePagerAdapter
同樣我們也設置viewPager.setOffscreenPageLimit(1);

FragmentStatePagerAdapter -- fragment初始化.png
此時我們向右滑動,讓 第1個 page顯示在界面上。

currentPage==1.png
此時我們向右滑動,讓 第2個 page顯示在界面上

currentPage==2.png
在這個時候,發現了與FragmentPagerAdapter不同的生命周期了。第0個page它執行了 onDestroy -> onDetach 。說明fragment是被真正的回收掉了
此時我們向右滑動,讓 第1個 page顯示在界面上

currentPage==1
為什么兩個適配器回收方式不同
通過之前對兩個適配器的了解,我們也知道了FragmentPagerAdapter 每一個生成的 Fragment 都將保存在內存之中 ,因此適用于那些相對靜態的頁,數量也比較少的那種;FragmentStatePagerAdapter將會對limit外的page進行回收。造成這兩者區別的原因就是因為
- instantiateItem(ViewGroup, int)
- destroyItem(ViewGroup, int, Object);
通過觀察兩個適配器的源碼,可以發現: 在FragmentPagerAdapter中

FragmentPagerAdapter
函數中判斷了一下要生成的framgment是否已經生成過了,如果生成過了,就使用舊的,舊的將被 Fragment.attach();如果沒有,就調用 getItem() 生成一個新的,新的對象將被 FragmentTransation.add()。
FragmentPagerAdapter 會將所有生成的 Fragment 對象通過 FragmentManager 保存起來備用,以后需要該 Fragment 時,都會從 FragmentManager 讀取, 而不會再次調用 getItem() 方法 。

destroyItem
該函數被調用后,會對 Fragment 進行 FragmentTransaction.detach()。這里不是 remove(),只是 detach(),因此 Fragment 還在 FragmentManager 管理中,Fragment 所占用的資源不會被釋放。
在FragmentStatePagerAdapter中,

instantiateItem

destroyItem
FragmentStatePagerAdapter 就是通過這種方式,每次都創建一個新的 Fragment,而在不用后就立刻釋放其資源,來達到節省內存占用的目的的。
總結
FragmentPagerAdapter 每一個生成的 Fragment 都將保存在內存之中 ,因此適用于那些相對靜態的頁,數量也比較少的那種;FragmentStatePagerAdapter將會對limit外的page進行回收。
我們可以通過觀察framgment的生命周期來進行判斷。
造成這樣的原因是因為: instantiateItem(ViewGroup, int) 和 destroyItem(ViewGroup, int, Object); 的實現方式不一樣
來自:http://www.jianshu.com/p/25a02f5a15b3