Fragment中的那些坑——Android進階
Fragment是Android在3.0(Homeycomb)版本時加入的用以更靈活的構建多螢幕介面的可UI元件。關於Fragment以基本使用方法可以參考官方的 和 ,以及 。 但是Fragment使用起來卻遠沒有教程中說的那樣簡單,也遠比Activity要複雜一些,這裡總結了孤在使用Fragment時所遇到的坑。
巢狀Fragment時Duplicated id或者Tag之癢
這是一個小坑,但是初學者很容易遇到,特別是在Fragment之中套有Fragment時,且又是佈局中新增子Fragment時更容易遇到。
現象:
Fragment中套有另一個Fragment,當第二次進入父Fragment時或者由Fragment建立的介面時會拋異常,大致意思是子Fragment的Id或Tag重複了。如果你在layout中給子fragment加了id或者tag,那麼一定會遇到此異常。
原因:
在新增Fragment時都可以為Fragment指定一個Id或者Tag用以標識這個Fragment。因為每個Activity所附帶的Fragment都是放在一個物件池中,在Activity的生命週期裡,Fragment仍然在池中,即使是把某一個Fragment從Activity中detach掉(也即用FragmentManager pop掉),這個池是由FragmentManager來管理的。當你再次要以某個id或者Tag新增Fragment時,FragmentManager會在池中檢索,如果發現已經存在Fragment物件帶有此Id或者Tag時,就會拋此異常並報怨Id重複。這麼做的目的就是減少物件的建立,儘可以的複用物件。
如何破解:
. 在佈局中寫fragment時,不要新增id或者tag;
-
. 如果非要新增id或者tag,就在程式碼中新增fragment,如使用Id或者Tag時,先到FragmentManager中查詢物件是否存在,不存在時再建立,也即:
Fragment target = getFragmentManager().findFragmentByTag("tag"); if (target == null) { targe = new SomeFragment(); } FragmentTransaction ft = getFragmentManager().beginTransaction(); ft.add(R.id.content, target, "tag"); ft.commit();
replace之痛
現象:
當有二個相同的整體頁面層疊時,想把最後一個佈局中的某個用Fragment來replace,會發現,它把前面的replace,後面的沒效果。
原因:
佈局的Id在一個窗體(Activity)中是唯一的,Fragment的replace也是使用此唯一的Id來把相應佈局替換成Fragment的。當相同的頁面層疊時,同一個Id的佈局出現了二次,但Id是一樣的。所以FragmentTransaction在replace時僅替換了一個。而不會像期待的那樣,替換最後一個頁面。
如何破解:
如果相同的頁面非要層疊,要麼不使用Fragment,要麼為佈局設定不同的Id。這種情況多出現在佈局的複用上面,比如某二個頁面長的像,所以複用了同一整體佈局。但實際的邏輯上不是相同的頁面,完全可以為佈局設定不同的Id。
可見性之疼
現象:
當有多個Fragment層疊在一起時,每個Fragment如何能感知其對使用者的可見性。比如應用有三個頁面,A,B和C,比如A是整體類別列表,B是每個類別的詳情,C又是類別的某種更詳細的資訊,當C顯示出來時,A和B怎麼能知道它其實對於使用者已經不可見了,所以就可以不重新整理,不載入資料等等。當C被使用者BACK後,B又如何感覺它變成可見了?
原因:
Fragment的生命週期與Activity是一樣的,新增到Activity會把OnCreate類似的回撥走一遍,然後,Activity onResume/onPause/onstart/onStop時,其所持有的Fragment也走相應的onResume/onPause/onstart/onPause。但是Fragment與Activity非常不同的是,Activity當有另一個Activity顯示時,當前的Activity會走onPause/onStop,而Fragment則完全沒有感知。最多隻能從FragmentManager那裡知道BackStackState改變了,但是是Fragment增加了,還是減少了,並不能知道。
如何破解:
這個一個非常令人蛋疼的問題,簡單的頁面還好,但是涉及到資料載入或者要針對某些事件(網路)重新整理時就有問題了,對使用者不可見的頁面沒必要重新整理。可行的解法就是:
. 監聽FragmentManager的BackStackState的改變
. 定義頁面路徑深度然後與BackStack深度比較,以感知是否對使用者可見 如前面A是一級,其path為1,B是2,C是3。當前Stack深度為3時,C是可見的,A與B不可見,以此類推。
空白區域的點選之膿
現象:
一個Fragment,層疊在另外一個Fragment或者Activity之上,此Fragment中有一些空白區域,也即Widget之外的空白區域,當點選這些空白區域的時候發現這個Fragment下面的Fragment或者Activity中的View收到了事件並且響應了點選事件。
原因:
Fragment的本質就是一個View佈局的管理器,當Fragment attach到Activity時,其實就是把Fragment#onCreateView()返回的View,替換掉(如果是用replace)FragmentTransaction#replace中指定的View,或者新增到(如果是add)FragmentTransaction#add()中指定的ViewGroup裡面。
當我們以層疊方式顯示多個Fragment時,通常的做法就是弄一個FrameLayout,然後每次把Fragment add到此佈局。因此,這時Activity的頁面佈局樹實際上就是一個FrameLayout裡面包含幾個View。
所以,當點選上面Fragment的空白區域時,如果事件沒被吃掉,就會向下傳遞。
如何破解:
在Fragment的根佈局加上一個clickable=true,這會讓根佈局把點選事件吃掉,以防止事件會繼續傳遞下去,造成上面的情況。
Activity重新建立之殤
現象:
這個沒有一般性的錯誤,只會有與專案相關的具體的錯誤異常,或者頁面顯示不正確。以及為什麼教程中都有這麼一句:
@OverrideonCreate(Bundle savedInstance) { if (savedIntance == null) { // create fragment and add it to Activity. }}
原因:
Activity除了正常啟動走到onCreate,還有另外的入口,比如系統配置資訊發生變化時,或者Activity在棧比較深的地方,系統會把Activity殺掉,然後再 重新建立 它,問題就是在這個重新建立。重新建立與新建一個Activity不同,它是要儘可能的恢復先前所在的狀態,因為這對使用者來說是透明的,也就是說不能讓使用者感知到,否則體驗會相當差。唯一與常規建立的區別就在於傳給onCreate的引數savedInstanceState是不是null.
如何破解:
為了能在Activity重建時恢復狀態,需要:
-
. 對於Activity
要在onSaveInstanceState()時,把一些變數儲存,然後在onCreate時恢復
-
. 對於Fragment
告訴系統,你想恢復狀態Fragment#setRetainInstance(true)。然後,也在onSavedInstance()中儲存狀態,在onCreate時恢復。 這就夠了,系統會在重新建立Activity時把其所持有的Fragment也建立出來。所以為什麼每個Fragment子類都需要定義一個預設的Constructor。更多的可以參考 。
FragmentTransaction的非同步操作之殤
FragmentTransaction是非同步的,commit()僅是相當於把操作加入到FragmentManager的佇列,然後FragmentManager會在某一個時刻來執行,並不是立即執行。所以,真正開始執行commit()時,如果Activity的生命週期發生了變化,比如走到了onPause,或者走到了onStop,或者onDestroy都走完了,那麼就會報出IllegalStateException。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/1762/viewspace-2816596/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Fragment中呼叫startActivityForResult的那些坑Fragment
- Android進階(七)Android中的ClassLoaderAndroid
- 掘金 AMA:聽《Android進階解密》作者--劉望舒聊 Android 開發、進階那些事Android解密
- Android加固和簽名的那些坑(防掉坑技巧)Android
- Fragment-踩坑Fragment
- Android之FragmentAndroidFragment
- Android中Fragment巢狀Fragment,切換Fragment時不顯示檢視的原因及解決方法AndroidFragment巢狀
- Java 中,Arrays 轉 List 的那些坑Java
- HTTP 規範中的那些暗坑HTTP
- Android高階進階之路【一】Android中View繪製流程淺析AndroidView
- 高階 Android 工程師的進階之路Android工程師
- Android基礎—FragmentAndroidFragment
- Android ListView 進階——從列表中獲取值AndroidView
- android appium微信等自動化的那些坑兒AndroidAPP
- Fragment時長統計那些事Fragment
- 那些前端工作中遇到的坑(01)前端
- 解決input 中placeholder的那些神坑
- 那些jdk中坑你沒商量的方法JDK
- Android--單Activity+多Fragment,玩轉FragmentAndroidFragment
- Android 碎片(Fragment)講解AndroidFragment
- PageHelper 分頁外掛使用中的那些“坑”
- 微軟外服札記④——Spark中的那些坑...微軟Spark
- Android開發教程 - 使用Data Binding(四)在Fragment中的使用AndroidFragment
- Python裡的那些坑Python
- Android:ListView的擴充與進階AndroidView
- Android進階:十四、熟悉Android打包編譯的流程Android編譯
- Android進階知識:ThreadLocalAndroidthread
- Android Gradle進階配置指南AndroidGradle
- Android 點將臺:撒豆成兵[- Fragment -]AndroidFragment
- Android Jetpack - Fragment官方說明AndroidJetpackFragment
- Android Studio 中那些最好用的外掛Android
- iOS開發的那些坑iOS
- Go語言的那些坑Go
- Laravel裡的那些坑 - OptionalLaravel
- 移動端的那些坑
- 再見,BLE的那些坑!
- jcenter上傳的那些坑
- Android Fragment用法知識點的講解AndroidFragment