国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

Android App中ViewPager與Fragment結(jié)合的一些問題解決

2020-01-02 06:58:43
字體:
供稿:網(wǎng)友

在了解ViewPager的工作原理之前,先回顧ListView的工作原理:

ListView只有在需要顯示某些列表項時,它才會去申請可用的視圖對象;如果為所有的列表項數(shù)據(jù)創(chuàng)建視圖對象,會浪費內(nèi)存;
ListView找誰去申請視圖對象呢? 答案是adapter。adapter是一個控制器對象,負責從模型層獲取數(shù)據(jù),創(chuàng)建并填充必要的視圖對象,將準備好的視圖對象返回給ListView;
首先,通過調(diào)用adapter的getCount()方法,ListView詢問數(shù)組列表中包含多少個對象(為避免出現(xiàn)數(shù)組越界的錯誤);緊接著ListView就調(diào)用adapter的getView(int, View, ViewGroup)方法。
ViewPager某種程度上類似于ListView,區(qū)別在于:ListView通過ArrayAdapter.getView(int position, View convertView, ViewGroup parent)填充視圖;ViewPager通過FragmentPagerAdapter.getItem(int position)生成指定位置的fragment.

而我們需要關注的是:
ViewPager和它的adapter是如何配合工作的?
聲明:本文內(nèi)容針對android.support.v4.app.*
繼承自android.support.v4.view.PagerAdapter,每頁都是一個Fragment,并且所有的Fragment實例一直保存在Fragment manager中。所以它適用于少量固定的fragment,比如一組用于分頁顯示的標簽。除了當Fragment不可見時,它的視圖層(view hierarchy)有可能被銷毀外,每頁的Fragment都會被保存在內(nèi)存中。(翻譯自代碼文件的注釋部分)
繼承自android.support.v4.view.PagerAdapter,每頁都是一個Fragment,當Fragment不被需要時(比如不可見),整個Fragment都會被銷毀,除了saved state被保存外(保存下來的bundle用于恢復Fragment實例)。所以它適用于很多頁的情況。(翻譯自代碼文件的注釋部分)
它倆的子類,需要實現(xiàn)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的管理略有不同,在詳細考察二者區(qū)別之前,我們通過兩種較為直觀的方式先感受下:

通過兩張圖片直觀的對比FragmentPagerAdapter和FragmentStatePagerAdapter的區(qū)別
說明:這兩張圖片來自于《Android權(quán)威編程指南》,原圖有3個Fragment,我增加了1個Fragment,以及被調(diào)到的方法。
FragmentPagerAdapter的Fragment管理:

2016323162934008.jpg (759×481)

FragmentStatePageAdapter的Fragment管理:

2016323163002573.jpg (751×587)

詳細分析 adapter method和fragment lifecycle method 的調(diào)用情況
好啦,感受完畢,我們需要探究其詳情,梳理adapter創(chuàng)建、銷毀Fragment的過程,過程中adapter method和fragment lifecycle method哪些被調(diào)到,有哪些一樣,有哪些不一樣。

最開始處于第0頁時,adapter不僅為第0頁創(chuàng)建Fragment實例,還為相鄰的第1頁創(chuàng)建了Fragment實例:

// 剛開始處在page0D/Adapter (25946): getItem(0)D/Fragment0(25946): newInstance(2015-09-10) // 注釋:newInstance()調(diào)用了Fragment的構(gòu)造器方法,下同。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頁創(chuàng)建Fragment實例;

// 第1次滑到page1D/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齊聲說:吶,請主公貳放心,屬下定會為您準備好相鄰的下一頁視圖噠!么么噠!
它倆對待下一頁的態(tài)度是相同的,但對于上上頁,它倆做出了不一樣的事情:

FragmentPagerAdapter說:上上頁的實例還保留著,只是銷毀了它的視圖:

// 第N次(N不等于1)向右滑動選中page2D/Adapter (25946): onPageSelected(2)D/Adapter (25946): destroyItem(0) // 銷毀page0的視圖D/Fragment0(25946): onDestroyView()D/Fragment3(25946): onCreateView() // page3的Fragment實例仍保存在FragmentManager中,所以只需創(chuàng)建它的視圖FragmentStatePagerAdapter說:上上頁的實例和視圖都被俺銷毀啦:// 第N次(N不等于1)向右滑選中page2D/Adapter (27880): onPageSelected(2)D/Adapter (27880): destroyItem(0) // 銷毀page0的實例和視圖D/Adapter (27880): getItem(3) // 創(chuàng)建page3的FragmentD/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就被調(diào)到,返回一個Fragment實例給adapter。
所以,有必要再次強調(diào),getItem是創(chuàng)建一個新的Fragment,但是這個方法名可能會被誤認為是返回一個已經(jīng)存在的Fragment。
對于FragmentPagerAdapter,當每頁的Fragment被創(chuàng)建后,這個函數(shù)就不會被調(diào)到了。對于FragmentStatePagerAdapter,由于Fragment會被銷毀,所以它仍會被調(diào)到。
由于我們必須在getItem中實例化一個Fragment,所以當getItem()被調(diào)用后,F(xiàn)ragment相應的生命周期函數(shù)也就被調(diào)到了:

D/Adapter (25946): getItem(1)D/Fragment1(25946): newInstance(Hello World, I'm li2.) // newInstance()調(diào)用了Fragment的構(gòu)造器方法;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。從源碼中可以看出二者的區(qū)別,一個detach,一個remove,這將調(diào)用到不同的Fragment生命周期函數(shù):

// 對于FragmentPagerAdapterD/Adapter (25946): onPageSelected(2)D/Adapter (25946): destroyItem(0)D/Fragment0(25946): onDestroyView() // 銷毀視圖// 對于FragmentStatePagerAdapterD/Adapter (27880): onPageSelected(2)D/Adapter (27880): destroyItem(0)D/Fragment0(27880): onDestroyView() // 銷毀視圖D/Fragment0(27880): onDestroy() // 銷毀實例D/Fragment0(27880): onDetach()FragmentPagerAdapter和FragmentStatePagerAdapter對比總結(jié)

二者使用方法基本相同,唯一的區(qū)別就在卸載不再需要的fragment時,采用的處理方式不同:

使用FragmentStatePagerAdapter會銷毀掉不需要的fragment。事務提交后,可將fragment從activity的FragmentManager中徹底移除。類名中的“state”表明:在銷毀fragment時,它會將其onSaveInstanceState(Bundle) 方法中的Bundle信息保存下來。用戶切換回原來的頁面后,保存的實例狀態(tài)可用于恢復生成新的fragment.
FragmentPagerAdapter的做法大不相同。對于不再需要的fragment,F(xiàn)ragmentPagerAdapter則選擇調(diào)用事務的detach(Fragment) 方法,而非remove(Fragment)方法來處理它。也就是說,F(xiàn)ragmentPagerAdapter只是銷毀了fragment的視圖,但仍將fragment實例保留在FragmentManager中。因此, FragmentPagerAdapter創(chuàng)建的fragment永遠不會被銷毀。
更新ViewPager中的Fragment
調(diào)用notifyDataSetChanged()時,2個adapter的方法的調(diào)用情況相同,當前頁和相鄰的兩頁的getItemPosition都會被調(diào)用到。

// 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;}

從網(wǎng)上找到的解決辦法是,覆寫getItemPosition使其返POSITION_NONE,以觸發(fā)Fragment的銷毀和重建。可是這將導致Fragment頻繁的銷毀和重建,并不是最佳的方法。
后來我把注意力放在了入口參數(shù)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);};// 更新界面時方法的調(diào)用情況// 當前頁為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)

在最開始調(diào)用notifyDataSetChanged試圖更新Fragment時,我是這樣做的:用arraylist保存所有的Fragment,當需要更新時,就從arraylist中取出Fragment,然后調(diào)用該Fragment的update方法。這種做法非常魚唇,當時完全不懂得adapter的Fragment manager在替我管理所有的Fragment。而我只需要:

  • 覆寫getCount告訴adapter有幾個Fragment;
  • 覆寫getItem以實例化一個指定位置的Fragment返回給adapter;
  • 覆寫getItemPosition,把入口參數(shù)強制轉(zhuǎn)型成自定義的Fragment,然后調(diào)用該Fragment的update方法以完成更新。

只需要覆寫這幾個adapter的方法,adapter會為你完成所有的管理工作,不需要自己保存、維護Fragment。

替換ViewPager中的Fragment
應用場景可能是這樣,比如有一組按鈕,Day/Month/Year,有一個包含幾個Fragment的ViewPager。點擊不同的按鈕,需要秀出不同的Fragment。
具體怎么實現(xiàn),請參考下面的代碼:
github.com/li2/Update_Replace_Fragment_In_ViewPager/ContainerFragment.java

一些誤區(qū)
ViewPager.getChildCount() 返回的是當前ViewPager所管理的沒有被銷毀視圖的Fragment,并不是所有的Fragment。想要獲取所有的Fragment數(shù)量,應該調(diào)用ViewPager.getAdapter().getCount().

ViewPager中使用Fragment+ListView,多次切換后造成ListView沒有數(shù)據(jù)顯示?

ViewPager+Fragment動態(tài)增刪緩存問題
產(chǎn)生原因:

我們在開發(fā)中會常常用到ViewPager+Fragment,有時候可能會有這樣的需求,需要對ViewPager中的內(nèi)容進行動態(tài)的增刪管理,但是我們都知道ViewPager為了保證滑動的流暢性,viewpager在加載當前頁的時候已經(jīng)將pager頁左右頁的內(nèi)容加載進內(nèi)存里了,所以此時我們不進行任何處理的話,是我發(fā)達到我們預期的效果的。
解決方案:

一、將FragmentPagerAdapter 替換成FragmentStatePagerAdapter, 因為前者只要加載過,fragment中的視圖就一直在內(nèi)存中,在這個過 程中無論你怎么刷新,清除都是無用的,直至程序退出; 后者可以滿足我們的需求。 2.我們可以重寫Adapter的方法

主站蜘蛛池模板: 新田县| 巴彦县| 济南市| 永宁县| 新野县| 枝江市| 汶上县| 维西| 伽师县| 盖州市| 湖口县| 始兴县| 花莲县| 水城县| 沽源县| 永州市| 温宿县| 西峡县| 青神县| 威宁| 义马市| 伽师县| 习水县| 宜君县| 武威市| 平远县| 洞头县| 宜都市| 明水县| 新河县| 贵南县| 武鸣县| 白山市| 青海省| 陕西省| 古浪县| 双峰县| 攀枝花市| 军事| 农安县| 佛坪县|