日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Fragment详解之四——管理Fragment(2)

發(fā)布時(shí)間:2025/6/15 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Fragment详解之四——管理Fragment(2) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

相關(guān)文章:

1、《Fragment詳解之一——概述》
2、《Fragment詳解之二——基本使用方法》
3、《Fragment詳解之三——管理Fragment(1)》
4、《Fragment詳解之四——管理Fragment(2)》
5、《Fragment詳解之五——Fragment間參數(shù)傳遞》
6、《Fragment詳解之六——如何監(jiān)聽fragment中的回退事件與怎樣保存fragment狀態(tài)》


上一篇,給大家講了有關(guān)Fragment管理的幾個(gè)函數(shù),即add,replace,remove,這節(jié)再講講其它函數(shù),然后再給大家看一個(gè)系統(tǒng)BUG。

一、hide()、show()

1、基本使用

這兩個(gè)函數(shù)的功能非常簡(jiǎn)單,

[java] view plaincopy
  • public?FragmentTransaction?hide(Fragment?fragment);//將指定的fragment隱藏不顯示???
  • public?FragmentTransaction?show(Fragment?fragment);//將以前hide()過的fragment顯示出來???
  • 先看下面的效果圖:

    • 首先,依次添加fragment1,fragment2,fragment3
    • 然后點(diǎn)擊”frag3 hide”,將fragment3隱藏不顯示,所以就顯示出來它的下一層fragment2的視圖
    • 然后再點(diǎn)擊“frag3 show”,將fragment3重新顯示出來
    • 然后點(diǎn)擊“frag2 hide”按鈕,將fragment2隱藏,但是由于fragment3覆蓋在fragment2之上,fragment2隱藏之后對(duì)fragment3沒有任何影響,所以在視圖上看不到任何效果。
    • 這時(shí)候,我們?cè)冱c(diǎn)擊“hide frag3”,將fragment3隱藏起來,這時(shí)候,由于fragment3和fragment2都隱藏了,所以顯示的就是fragment1的視圖。
    • 最后,點(diǎn)擊“frag2 show”將fragment2顯示出來?

    代碼如下:
    (1)、同樣是新建三個(gè)fragment,命名為Fragment1,Fragment2,Fragment3,同樣是用背景色和文字來區(qū)別;
    (2)、然后是點(diǎn)擊“add frag1”按鈕的代碼

    [java] view plaincopy
  • Fragment1?fragment1?=?new?Fragment1();??
  • addFragment(fragment1,?"fragment1");??
  • 其中:
    [java] view plaincopy
  • private?void?addFragment(Fragment?fragment,?String?tag)?{??
  • ????FragmentManager?manager?=?getSupportFragmentManager();??
  • ????FragmentTransaction?transaction?=?manager.beginTransaction();??
  • ????transaction.add(R.id.fragment_container,fragment,?tag);??
  • ????transaction.addToBackStack(tag);??
  • ????transaction.commit();??
  • }??
  • 這個(gè)函數(shù)已經(jīng)在前面幾章用過N多次了,就不再講了。
    (3)、frag3 hide的代碼:
    [java] view plaincopy
  • FragmentManager?manager?=?getSupportFragmentManager();??
  • Fragment?fragment?=?manager.findFragmentByTag("fragment3");??
  • FragmentTransaction?transaction?=?manager.beginTransaction();??
  • transaction.hide(fragment);??
  • transaction.addToBackStack("hide?fragment3");??
  • transaction.commit();??
  • 也沒什么難度,跟前面幾篇不一樣的地方就是調(diào)用了transaction.hide(fragment);函數(shù);
    (4)、frag3 show的代碼:
    [java] view plaincopy
  • FragmentManager?manager?=?getSupportFragmentManager();??
  • Fragment?fragment?=?manager.findFragmentByTag("fragment3");??
  • FragmentTransaction?transaction?=?manager.beginTransaction();??
  • transaction.show(fragment);??
  • transaction.addToBackStack("show?fragment3");??
  • transaction.commit();??
  • 這里也基本上與以前的操作代碼都一樣,只是使用了transaction.show(fragment);函數(shù)

    2、在實(shí)戰(zhàn)中的運(yùn)用方法

    如果我們使用replace來切換頁(yè)面,那么在每次切換的時(shí)候,Fragment都會(huì)重新實(shí)例化,重新加載一邊數(shù)據(jù),這樣非常消耗性能和用戶的數(shù)據(jù)流量。
    這是因?yàn)閞eplace操作,每次都會(huì)把container中的現(xiàn)有的fragment實(shí)例清空,然后再把指定的fragment添加進(jìn)去,就就造成了在切換到以前的fragment時(shí),就會(huì)重新實(shí)例會(huì)fragment。
    正確的切換方式是add(),切換時(shí)hide(),add()另一個(gè)Fragment;再次切換時(shí),只需hide()當(dāng)前,show()另一個(gè)。
    這樣就能做到多個(gè)Fragment切換不重新實(shí)例化:(基本算法如下)

    [java] view plaincopy
  • public?void?switchContent(Fragment?from,?Fragment?to)?{??
  • ????if?(!to.isAdded())?{????//?先判斷是否被add過??
  • ????????transaction.hide(from).add(R.id.content_frame,?to).commit();?//?隱藏當(dāng)前的fragment,add下一個(gè)到Activity中??
  • ????}?else?{??
  • ????????transaction.hide(from).show(to).commit();?//?隱藏當(dāng)前的fragment,顯示下一個(gè)??
  • ????}??
  • }??
  • 大家可能覺得這里有個(gè)問題,如果我們要show()的fragment不在最頂層怎么辦?如果不在ADD隊(duì)列的隊(duì)首,那顯然show()之后是不可見的;那豈不影響了APP邏輯。大家有這個(gè)想法是很棒的,但在APP中不存在這樣的情況,因?yàn)槲覀兊腁PP的fragment是一層層ADD進(jìn)去的,而且我們的fragment實(shí)例都是唯一的,用TAG來標(biāo)識(shí),當(dāng)退出的時(shí)候也是一層層剝離的,所以當(dāng)用戶的動(dòng)作導(dǎo)致要添加某個(gè)fragment時(shí),那說明這個(gè)fragment肯定是在棧頂?shù)摹?

    二、detach()、attach()

    這兩個(gè)函數(shù)的聲明如下:

    [java] view plaincopy
  • public?FragmentTransaction?detach(Fragment?fragment);??
  • public?abstract?FragmentTransaction?attach(Fragment?fragment);??
  • detach(): 會(huì)將view與fragment分離,將此將view從viewtree中刪除!而且將fragment從Activity的ADD隊(duì)列中移除!所以在使用detach()后,使用fragment::isAdded()返回的值是false;但此fragment實(shí)例并不會(huì)刪除,此fragment的狀態(tài)依然保持著使用,所以在fragmentManager中仍然可以找到,即通過FragmentManager::findViewByTag()仍然是會(huì)有值的。
    attach(): 顯然這個(gè)方法與detach()所做的工作相反,它一方面利用fragment的onCreateView()來重建視圖,一方面將此fragment添加到ADD隊(duì)列中;這里最值得注意的地方在這里:由于是將fragment添加到ADD隊(duì)列,所以只能添加到列隊(duì)頭部,所以attach()操作的結(jié)果是,最新操作的頁(yè)面始終顯示在最前面!這也就解釋了下面的例子中,為了fragment2 detach()后,當(dāng)再次attach()后,卻跑到了fragment3的前面的原因。還有,由于這里會(huì)將fragment添加到Activity的ADD隊(duì)列中,所以在這里調(diào)用fragment::isAdded()將返回True;
    下面用一個(gè)例子來講講,有關(guān)這上面所講解的知識(shí),效果圖如下:

    • (1)、同樣,先依次添加Fragment1,Fragment2,Fragment3
    • (2)、然后點(diǎn)擊“frag3 detach”,將fragment3的View視圖刪除,然后從ADD隊(duì)列中將fragment移除。之后點(diǎn)擊“fragment is added?”根據(jù)TOAST可以看出,fragment::isAdded()函數(shù)返回值是false;
    • (3)、然后點(diǎn)擊“frag3 attach”,將fragment重新與Activity綁定,它有兩個(gè)動(dòng)作,一方面重建fragment視圖,一方面將fragment添加到Activity的ADD隊(duì)列中;所以這時(shí)候點(diǎn)擊“fragment is added?”,fragment::isAdded()函數(shù)返回值是true;
    • (4)、然后點(diǎn)擊“frag2 detach”,由于fragment2在fragment3之下,所以給fragment2使用detach,在界面上看不出任何效果。
    • (5)、但當(dāng)點(diǎn)擊“frag2 attach”時(shí),問題出現(xiàn)了,由于attach()會(huì)做兩件事,一方面重建fragment視圖,一方面將fragment添加到Activity的ADD隊(duì)列中;由于是ADD隊(duì)列,所以肯定添加的位置肯定在隊(duì)首;所以fragment2就顯示在了最上方,把fragment3蓋住了,這就是為什么在點(diǎn)擊“frag2 attach”之后,卻可以看到fragment2的視圖的原因!?
    好了,下面就是代碼部分了,這部分代碼是在上一部分的上面添加了幾個(gè)按鈕而來的,直接看按鈕點(diǎn)擊時(shí)的代碼操作:

    1、點(diǎn)擊frag3 detach按鈕的代碼

    [java] view plaincopy
  • FragmentManager?manager?=?getSupportFragmentManager();??
  • Fragment?fragment?=?manager.findFragmentByTag("fragment3");??
  • FragmentTransaction?transaction?=?manager.beginTransaction();??
  • transaction.detach(fragment);??
  • transaction.addToBackStack("detach?fragment3");??
  • transaction.commit();??
  • 從代碼也可以看到,沒什么難度,這個(gè)函數(shù)的最難點(diǎn)在于知道detach()在執(zhí)行過程中都干了什么!再重申一遍:一方面刪除fragment的View視圖;一方面將fragment從Activity的ADD隊(duì)列中移除!說是Activity的ADD隊(duì)列,倒不如說是container的ADD隊(duì)列更貼切些;因?yàn)橐粋€(gè)Activity上面可以有多個(gè)Container來盛裝Fragment實(shí)例組,每一個(gè)Container都會(huì)被分配一個(gè)ADD隊(duì)列來記錄當(dāng)前通過add()方法,添加到這個(gè)container里的所有fragment實(shí)例。
    2、點(diǎn)擊“frag3 attach”按鈕的代碼
    [java] view plaincopy
  • FragmentManager?manager?=?getSupportFragmentManager();??
  • Fragment?fragment?=?manager.findFragmentByTag("fragment3");??
  • FragmentTransaction?transaction?=?manager.beginTransaction();??
  • transaction.attach(fragment);??
  • transaction.addToBackStack("attach?fragment3");??
  • transaction.commit();??
  • 依然,相比以前的fragment操作也只多了一個(gè)transaction.attach(fragment);沒什么難度。關(guān)鍵仍然在于知道attach()操作都做了哪些事!再次重申:一方面重建fragment的View,注意是重建!另一方面,將fragment實(shí)例添加進(jìn)container的ADD隊(duì)列中;關(guān)于"frag2 detach"和"frag2 attach"的代碼就不再貼出來了,跟frag3的一樣。
    好了,到這里,有關(guān)Fragment的操作都已經(jīng)講完了,下面就講講有關(guān)在Fragment操作中Android的BUG!
    源碼在文章底部給出

    三、系統(tǒng)BUG——add()和replace()千萬不要共用!!!

    先寫個(gè)例子來看一下問題:
    這個(gè)例子分為兩部分,


    • 第一部分:先利用add()函數(shù),依次add進(jìn)去fragment1,fragment2,fragment3,fragment4,fragment5,然后利用"print back stack"打印出當(dāng)前在回退棧中每次操作的名稱;每回退一次打一次回退棧內(nèi)容,可見一切都是正常的,即回退棧頂?shù)捻?xiàng),正是當(dāng)前VIEW頂部顯示的內(nèi)容。
    • 第二部分,如果我們先利用add()函數(shù),依次add進(jìn)去fragment1,fragment2,fragment3,fragment4,然后再利用replace函數(shù)添加進(jìn)去fragment5;然后利用"print back stack"打印出當(dāng)前在回退棧中每次操作的名稱;可以看到,當(dāng)回退棧頂是"add fragment4"時(shí),fragment4卻沒有出現(xiàn),點(diǎn)擊返回按鈕,卻把這個(gè)"add fragment4"的Transaction操作給返回了。同樣的現(xiàn)象也發(fā)生在fragment2中;

    還是先看看實(shí)現(xiàn)代碼:
    1、添加fragment,比如添加fragment1,其它fragment2,fragment3,fragment4同理

    [java] view plaincopy
  • Fragment1?fragment1?=?new?Fragment1();??
  • addFragment(fragment1,?"add?fragment1");??
  • 其中:
    [java] view plaincopy
  • private?void?addFragment(Fragment?fragment,?String?tag)?{??
  • ????FragmentManager?manager?=?getSupportFragmentManager();??
  • ????FragmentTransaction?transaction?=?manager.beginTransaction();??
  • ????transaction.add(R.id.fragment_container,?fragment);??
  • ????transaction.addToBackStack(tag);??
  • ????transaction.commit();??
  • }??
  • 2、replace fragment5:
    代碼上沒什么難度,在添加到回退棧時(shí),添加TAG:"replace fragment5"
    [java] view plaincopy
  • Fragment5?fragment5?=?new?Fragment5();??
  • FragmentManager?manager?=?getSupportFragmentManager();??
  • FragmentTransaction?transaction?=?manager.beginTransaction();??
  • transaction.replace(R.id.fragment_container,?fragment5);??
  • transaction.addToBackStack("replace?fragment5");??
  • transaction.commit();??
  • 3、打印出回退棧中的內(nèi)容:
    這里要講一個(gè)函數(shù)了:
    [java] view plaincopy
  • public?int?getBackStackEntryCount();//獲取回退棧中,Transaction回退操作的數(shù)量??
  • public?BackStackEntry?getBackStackEntryAt(int?index);//根據(jù)索引得到回退棧變量??
  • 其中g(shù)etBackStackEntryAt()返回的變量BackStackEntry,就是回退棧中保存每次transaction操作的變量;它有很多方法,其中BackStackEntry::getName()是獲取Transacion操作的名字,即通過transaction.addToBackStack("replace fragment5");傳進(jìn)去的字符串。關(guān)于BackStackEntry的其它方法,靠大家自己去發(fā)掘啦,這個(gè)函數(shù)用的不多,就不再細(xì)講了。
    [java] view plaincopy
  • TextView?tv?=?(TextView)?findViewById(R.id.tv_stack_val);??
  • ??
  • FragmentManager?manager?=?getSupportFragmentManager();??
  • int?count?=?manager.getBackStackEntryCount();??
  • StringBuilder?builder?=?new?StringBuilder("回退棧內(nèi)容為:\n");??
  • for?(int?i?=?--count;i>=0;i--){??
  • ????FragmentManager.BackStackEntry?entry=?manager.getBackStackEntryAt(i);??
  • ????builder.append(entry.getName()+"\n");??
  • }??
  • tv.setText(builder.toString());??
  • 好啦,代碼看完了,要講問題了。那問題來了,為什么在回退棧中有add fragment4和add fragment2的操作,卻不顯示呢?
    問題出在了replace()操作上,replace()操作原意的實(shí)現(xiàn)應(yīng)該是清空container中所有的fragment實(shí)例,然后再將指定的fragment添加到container的ADD隊(duì)列中;但在清空時(shí),他們的代碼是這樣寫的:
    [java] view plaincopy
  • for?(int?i=0;i<mManager.mAdded.size();?i++)?{??
  • ????Fragment?old?=?mManager.mAdded.get(i);??
  • ????……??
  • ????mManager.removeFragment(old,?mTransition,?mTransitionStyle);??
  • }??
  • 其中:mAdded就是我們前面說的container的ADD隊(duì)列;看他的操作:
    首先,先逐個(gè)得到mAdded隊(duì)列中的fragment,即:
    [java] view plaincopy
  • Fragment?old?=?mManager.mAdded.get(i);??
  • 然后,將這個(gè)fragment實(shí)例移除:
    [java] view plaincopy
  • mManager.removeFragment(old,?mTransition,?mTransitionStyle);??
  • 有沒有看出什么問題?他把這個(gè)fragment從mAdded隊(duì)列中直接移除了!!!!那這不打亂了原來的順序了么,在移除下一個(gè)fragment時(shí)就根本對(duì)不上號(hào)了。看不懂?沒關(guān)系,我們舉個(gè)例子來講:
    比如,我們上面的,在mAdded隊(duì)列中有1,2,3,4,5這五個(gè)fragment;
    首先,當(dāng)i=0時(shí),移除1,這沒錯(cuò)!但它是將mAdded隊(duì)列中的1直接移除的哦!所以移除1以后,mAdded隊(duì)列的值變成了2,3,4,5
    這時(shí)候,當(dāng)i=1時(shí),刪除的是3!!!!知道問題所在了吧!所以在刪除3后,mAdded隊(duì)列的值為2,4,5
    所以當(dāng)i=2時(shí),刪除的是5!!!!所以這就造成了為什么我們的fragment2和fragment4明明在回退棧中,即顯示不出來的原因,因?yàn)樗麄冊(cè)趧h除時(shí)根本就沒有刪除,而在回退棧回退時(shí)卻又要跟著操作順序來回退,即remove fragment5,逐個(gè)add進(jìn)去fragment4,fragment3,fragment2,fragment1,而又由于fragment4和fragment2沒有被刪除,所以出現(xiàn)了錯(cuò)誤,導(dǎo)致系統(tǒng)哪里出了問題,所以顯示不出來,至于是哪里出了問題,我也不知道,因?yàn)槲覈L試了show()和attach() fragment4都還是沒有效果。可能是系統(tǒng)底層的問題吧。所以,這里忠告大家,add()和replace()不能共用!!!!!

    有關(guān)這個(gè)系統(tǒng)BUG的細(xì)節(jié)分析,請(qǐng)參考文章:

    1、《Fragment(五)Transaction 源碼分析》

    2、《報(bào)給GOOGLE的BUG地址》

    總結(jié)

    以上是生活随笔為你收集整理的Fragment详解之四——管理Fragment(2)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。