參考連接 (一) 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;
}
}
//////////////////////////////數組
22.1k 次閱讀 · 讀完須要 50 分鐘app
10ide
How to update and replace fragment in viewpager?函數
在瞭解ViewPager的工做原理以前,先回顧ListView的工做原理:this
ListView只有在須要顯示某些列表項時,它纔會去申請可用的視圖對象;若是爲全部的列表項數據建立視圖對象,會浪費內存;google
ListView找誰去申請視圖對象呢? 答案是adapter。adapter是一個控制器對象,負責從模型層獲取數據,建立並填充必要的視圖對象,將準備好的視圖對象返回給ListView;
首先,經過調用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.
而咱們須要關注的是:
聲明:本文內容針對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()
.
稍後作詳細分析:
// 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的管理略有不一樣,在詳細考察兩者區別以前,咱們經過兩種較爲直觀的方式先感覺下:
說明:這兩張圖片來自於《Android權威編程指南》,原圖有3個Fragment,我增長了1個Fragment,以及被調到的方法。
FragmentPagerAdapter的Fragment管理:
FragmentStatePageAdapter的Fragment管理:
好啦,感覺完畢,咱們須要探究其詳情,梳理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()
// 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()
// 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()
兩者使用方法基本相同,惟一的區別就在卸載再也不須要的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》)
調用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)