研磨FragmentPagerAdapter的方法

FragmentPagerAdapter的三個調(diào)用方法

為了解決不同ViewPage 和Fragment之間切換時所引起的問題作彤,F(xiàn)ragmentPagerAdapter提供了一套相當(dāng)簡單易用的調(diào)用方法膘魄,讓頁面切換變得簡單易用乌逐。

有三點必須注意:

首先,ViewPager必須是手指橫向滑動操作瓣距。

其次是黔帕,每個頁面都必須由獨的fragment class去繼承。它繼承自android.support.v4.view.PagerAdapter蹈丸,每頁都是一個Fragment成黄,并且所有的Fragment實例一直保存在Fragment manager中。所以它適用于少量固定的fragment逻杖,比如一組用于分頁顯示的標(biāo)簽奋岁。除了當(dāng)Fragment不可見時,它的視圖層(view hierarchy)有可能被銷毀外荸百,每頁的Fragment都會被保存在內(nèi)存中闻伶。

第三是耗用內(nèi)存的問題。在使用FragmentPagerAdapter ?時够话,F(xiàn)ragment對象會一直存留在內(nèi)存中蓝翰,所以當(dāng)有大量的顯示頁時,就不適合用FragmentPagerAdapter 了女嘲,F(xiàn)ragmentPagerAdapter ?適用于只有少數(shù)的page情況畜份,像選項卡。

讓我們看看調(diào)用方法:


?// 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

}

}


【ViewPager】

ViewPager 如其名所述欣尼,是負責(zé)翻頁的一個 View爆雹。準確說是一個 ViewGroup,包含多個 View 頁愕鼓,在手指橫向滑動屏幕時钙态,其負責(zé)對 View 進行切換。為了生成這些 View 頁菇晃,需要提供一個 PagerAdapter 來進行和數(shù)據(jù)綁定以及生成最終的 View 頁册倒。

setAdapter()

ViewPager 通過 setAdapter() 來建立與 PagerAdapter 的聯(lián)系。這個聯(lián)系是雙向的磺送,一方面剩失,ViewPager 會擁有 PagerAdapter 對象,從而可以在需要時調(diào)用 PagerAdapter 的方法册着;另一方面,ViewPager 會在 setAdapter() 中調(diào)用 PagerAdapter 的 registerDataSetObserver() 方法脾歧,注冊一個自己生成的 PagerObserver 對象甲捏,從而在 PagerAdapter 有所需要時(如 notifyDataSetChanged() 或 notifyDataSetInvalidated() 時),可以調(diào)用 Observer 的 onChanged() 或 onInvalidated() 方法鞭执,從而實現(xiàn) PagerAdapter 向 ViewPager 方向發(fā)送信息司顿。

dataSetChanged()

在 PagerObserver.onChanged()芒粹,以及 PagerObserver.onInvalide() 中被調(diào)用。因此當(dāng) PagerAdapter.notifyDataSetChanged() 被觸發(fā)時大溜,ViewPager.dataSetChanged() 也可以被觸發(fā)化漆。該函數(shù)將使用 getItemPosition() 的返回值來進行判斷,如果為 POSITION_UNCHANGED钦奋,則什么都不做座云;如果為 POSITION_NONE,則調(diào)用 PagerAdapter.destroyItem() 來去掉該對象付材,并設(shè)置為需要刷新 (needPopulate = true) 以便觸發(fā) PagerAdapter.instantiateItem() 來生成新的對象朦拖。

【PagerAdapter】

PageAdapter 是 ViewPager 的支持者,ViewPager 將調(diào)用它來取得所需顯示的頁厌衔,而 PageAdapter 也會在數(shù)據(jù)變化時璧帝,通知 ViewPager。這個類也是FragmentPagerAdapter 以及 FragmentStatePagerAdapter 的基類富寿。如果繼承自該類睬隶,至少需要實現(xiàn) instantiateItem(), destroyItem(), getCount() 以及 isViewFromObject()。


getItemPosition()

該函數(shù)用以返回給定對象的位置页徐,給定對象是由 instantiateItem() 的返回值苏潜。

在 ViewPager.dataSetChanged() 中將對該函數(shù)的返回值進行判斷,以決定是否最終觸發(fā) PagerAdapter.instantiateItem() 函數(shù)泞坦。

在 PagerAdapter 中的實現(xiàn)是直接傳回 POSITION_UNCHANGED窖贤。如果該函數(shù)不被重載,則會一直返回 POSITION_UNCHANGED贰锁,從而導(dǎo)致 ViewPager.dataSetChanged() 被調(diào)用時赃梧,認為不必觸發(fā) PagerAdapter.instantiateItem()。很多人因為沒有重載該函數(shù)豌熄,而導(dǎo)致調(diào)用

PagerAdapter.notifyDataSetChanged() 后授嘀,什么都沒有發(fā)生。

instantiateItem()

在每次 ViewPager 需要一個用以顯示的 Object 的時候锣险,該函數(shù)都會被 ViewPager.addNewItem() 調(diào)用蹄皱。

notifyDataSetChanged()

在數(shù)據(jù)集發(fā)生變化的時候,一般 Activity 會調(diào)用 PagerAdapter.notifyDataSetChanged()芯肤,以通知 PagerAdapter巷折,而 PagerAdapter 則會通知在自己這里注冊過的所有 DataSetObserver尤辱。其中之一就是在 ViewPager.setAdapter() 中注冊過的 PageObserver闽颇。PageObserver 則進而調(diào)用 ViewPager.dataSetChanged(),從而導(dǎo)致 ViewPager 開始觸發(fā)更新其內(nèi)含 View 的操作下愈。

【FragmentPagerAdapter】

FragmentPagerAdapter 繼承自 PagerAdapter。相比通用的 PagerAdapter署拟,該類更專注于每一頁均為 Fragment 的情況婉宰。如文檔所述,該類內(nèi)的每一個生成的 Fragment 都將保存在內(nèi)存之中推穷,因此適用于那些相對靜態(tài)的頁心包,數(shù)量也比較少的那種;如果需要處理有很多頁馒铃,并且數(shù)據(jù)動態(tài)性較大蟹腾、占用內(nèi)存較多的情況,應(yīng)該使用FragmentStatePagerAdapter骗露。FragmentPagerAdapter 重載實現(xiàn)了幾個必須的函數(shù)岭佳,因此來自 PagerAdapter 的函數(shù),我們只需要實現(xiàn) getCount()萧锉,即可珊随。且,由于 FragmentPagerAdapter.instantiateItem() 的實現(xiàn)中柿隙,調(diào)用了一個新增的虛函數(shù) getItem()叶洞,因此,我們還至少需要實現(xiàn)一個 getItem()禀崖。因此衩辟,總體上來說,相對于繼承自 PagerAdapter波附,更方便一些艺晴。

getItem()

該類中新增的一個虛函數(shù)。函數(shù)的目的為生成新的 Fragment 對象掸屡。重載該函數(shù)時需要注意這一點封寞。在需要時,該函數(shù)將被 instantiateItem() 所調(diào)用仅财。

如果需要向 Fragment 對象傳遞相對靜態(tài)的數(shù)據(jù)時狈究,我們一般通過 Fragment.setArguments() 來進行,這部分代碼應(yīng)當(dāng)放到 getItem()盏求。它們只會在新生成 Fragment 對象時執(zhí)行一遍抖锥。

如果需要在生成 Fragment 對象后,將數(shù)據(jù)集里面一些動態(tài)的數(shù)據(jù)傳遞給該 Fragment碎罚,那么磅废,這部分代碼不適合放到 getItem() 中。因為當(dāng)數(shù)據(jù)集發(fā)生變化時荆烈,往往對應(yīng)的 Fragment 已經(jīng)生成还蹲,如果傳遞數(shù)據(jù)部分代碼放到了 getItem() 中,這部分代碼將不會被調(diào)用。這也是為什么很多人發(fā)現(xiàn)調(diào)用 PagerAdapter.notifyDataSetChanged() 后谜喊,getItem() 沒有被調(diào)用的一個原因。

instantiateItem()

函數(shù)中判斷一下要生成的 Fragment 是否已經(jīng)生成過了倦始,如果生成過了斗遏,就使用舊的,舊的將被 Fragment.attach()鞋邑;如果沒有诵次,就調(diào)用 getItem() 生成一個新的,新的對象將被 FragmentTransation.add()枚碗。

FragmentPagerAdapter 會將所有生成的 Fragment 對象通過 FragmentManager 保存起來備用逾一,以后需要該 Fragment 時,都會從 FragmentManager 讀取肮雨,而不會再次調(diào)用 getItem() 方法遵堵。

如果需要在生成 Fragment 對象后,將數(shù)據(jù)集中的一些數(shù)據(jù)傳遞給該 Fragment怨规,這部分代碼應(yīng)該放到這個函數(shù)的重載里陌宿。在我們繼承的子類中,重載該函數(shù)波丰,并調(diào)用 FragmentPagerAdapter.instantiateItem() 取得該函數(shù)返回 Fragment 對象壳坪,然后,我們該 Fragment 對象中對應(yīng)的方法掰烟,將數(shù)據(jù)傳遞過去爽蝴,然后返回該對象。

否則纫骑,如果將這部分傳遞數(shù)據(jù)的代碼放到 getItem()中蝎亚,在 PagerAdapter.notifyDataSetChanged() 后,這部分數(shù)據(jù)設(shè)置代碼將不會被調(diào)用惧磺。

destroyItem()

該函數(shù)被調(diào)用后颖对,會對 Fragment 進行 FragmentTransaction.detach()。這里不是 remove()磨隘,只是 detach()缤底,因此 Fragment 還在 FragmentManager 管理中,F(xiàn)ragment 所占用的資源不會被釋放番捂。

【FragmentStatePagerAdapter】

FragmentStatePagerAdapter 和前面的 FragmentPagerAdapter 一樣个唧,是繼承子 PagerAdapter。但是设预,和 FragmentPagerAdapter 不一樣的是徙歼,正如其類名中的 'State' 所表明的含義一樣,該 PagerAdapter 的實現(xiàn)將只保留當(dāng)前頁面,當(dāng)頁面離開視線后魄梯,就會被消除桨螺,釋放其資源;而在頁面需要顯示時酿秸,生成新的頁面(就像 ListView 的實現(xiàn)一樣)灭翔。這么實現(xiàn)的好處就是當(dāng)擁有大量的頁面時,不必在內(nèi)存中占用大量的內(nèi)存辣苏。

getItem()

一個該類中新增的虛函數(shù)肝箱。

函數(shù)的目的為生成新的 Fragment 對象。

Fragment.setArguments() 這種只會在新建 Fragment 時執(zhí)行一次的參數(shù)傳遞代碼稀蟋,可以放在這里煌张。

由于 FragmentStatePagerAdapter.instantiateItem() 在大多數(shù)情況下,都將調(diào)用 getItem() 來生成新的對象退客,因此如果在該函數(shù)中放置與數(shù)據(jù)集相關(guān)的 setter 代碼骏融,基本上都可以在 instantiateItem() 被調(diào)用時執(zhí)行,但這和設(shè)計意圖不符井辜。畢竟還有部分可能是不會調(diào)用 getItem() 的绎谦。因此這部分代碼應(yīng)該放到 instantiateItem() 中。

instantiateItem()

除非碰到 FragmentManager 剛好從 SavedState 中恢復(fù)了對應(yīng)的 Fragment 的情況外粥脚,該函數(shù)將會調(diào)用 getItem() 函數(shù)窃肠,生成新的 Fragment 對象。新的對象將被 FragmentTransaction.add()刷允。

FragmentStatePagerAdapter 就是通過這種方式冤留,每次都創(chuàng)建一個新的 Fragment,而在不用后就立刻釋放其資源树灶,來達到節(jié)省內(nèi)存占用的目的的纤怒。

destroyItem()

將 Fragment 移除,即調(diào)用 FragmentTransaction.remove()天通,并釋放其資源泊窘。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市像寒,隨后出現(xiàn)的幾起案子烘豹,更是在濱河造成了極大的恐慌,老刑警劉巖诺祸,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件携悯,死亡現(xiàn)場離奇詭異,居然都是意外死亡筷笨,警方通過查閱死者的電腦和手機憔鬼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門龟劲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人轴或,你說我怎么就攤上這事昌跌。” “怎么了照雁?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵避矢,是天一觀的道長。 經(jīng)常有香客問我囊榜,道長,這世上最難降的妖魔是什么亥宿? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任卸勺,我火速辦了婚禮,結(jié)果婚禮上烫扼,老公的妹妹穿的比我還像新娘曙求。我一直安慰自己,他們只是感情好映企,可當(dāng)我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布悟狱。 她就那樣靜靜地躺著,像睡著了一般堰氓。 火紅的嫁衣襯著肌膚如雪挤渐。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天双絮,我揣著相機與錄音浴麻,去河邊找鬼。 笑死囤攀,一個胖子當(dāng)著我的面吹牛软免,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播焚挠,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼膏萧,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蝌衔?” 一聲冷哼從身側(cè)響起榛泛,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎胚委,沒想到半個月后挟鸠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡亩冬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年艘希,在試婚紗的時候發(fā)現(xiàn)自己被綠了硼身。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡覆享,死狀恐怖佳遂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情撒顿,我是刑警寧澤丑罪,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站凤壁,受9級特大地震影響吩屹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拧抖,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一煤搜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧唧席,春花似錦擦盾、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至徒仓,卻和暖如春腐碱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蓬衡。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工喻杈, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人狰晚。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓筒饰,卻偏偏與公主長得像,于是被迫代替她去往敵國和親壁晒。 傳聞我的和親對象是個殘疾皇子瓷们,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容