天天看點

對于Fragment的一些了解

Fragment想必大家不陌生吧,在日常開發中,對于Fragment的使用也很頻繁,現在主流的APP中,基本的架構也都是一個首頁,然後每個Tab項用Fragment做布局,不同選項做切換,使用起來也友善。但是否對它有足夠的認識嗎,谷歌推薦用Fragment來代替Activity,但又沒有明确說為什麼要用Fragment來代替Activity,這裡就引發争議了,那到底是要不要用,是否使用Fragment完全替換Activity真的比正常開發模式更好嗎?如果要用的話,那需要了解為何要使用Fragment,Fragment是什麼,它的生命周期如何,如何使用,通信又是怎樣,有什麼缺點嗎?帶着這些問題,我們一一去解讀。

Fragment為何要用

Fragment是什麼

Fragment生命周期

Fragment怎麼用

Fragment通信

Fragment是否很完美

小結

參考位址

Fragment是Android 3.0 (Honeycomb)被引入的。主要目的是為了給大螢幕(如平闆電腦)上更加動态和靈活的UI設計提供支援。由于平闆電腦的螢幕比手機的螢幕大很多,是以可用于組合和交換的UI元件的空間更大,利用Fragment實作此類設計的時,就無需管理對視圖層次結構的複雜更改。

通過将 Activity 布局分成片段,您可以在運作時修改 Activity 的外觀,并在由 Activity 管理的傳回棧中保留這些更改。如果僅僅隻有Activity布局,那是不夠的,不僅在手機上有一套布局,同時在平闆上還需要設計一套布局,那樣維護起來也麻煩,代碼上也有一定的備援,對于APP包的大小也有一定的壓力。Fragment的優勢是布局在不同裝置上的适配。

比如:

對于Fragment的一些了解

從圖中我們可以看到,在平闆中,一個Activity A包含了兩個Fragment,分别是Fragment A和Fragment B,但在手機中呢,就需要兩個Activity,分别是Activity A包含Fragment A和Activity B包含Fragment B。同時每個Fragment都具有自己的一套生命周期回調方法,并各自處理自己的使用者輸入事件。 是以,在平闆中使用一個Activity 就可以了,左側是清單,右邊是内容詳情。

除此之外,使用Fragment還有這麼幾個方面優勢:

代碼複用。特别适用于子產品化的開發,因為一個Fragment可以被多個Activity嵌套,有個共同的業務子產品就可以複用了,是子產品化UI的良好元件。

Activity用來管理Fragment。Fragment的生命周期是寄托到Activity中,Fragment可以被Attach添加和Detach釋放。

可控性。Fragment可以像普通對象那樣自由的建立和控制,傳遞參數更加容易和友善,也不用處理系統相關的事情,顯示方式、替換、不管是整體還是部分,都可以做到相應的更改。

Fragments是view controllers,它們包含可測試的,解耦的業務邏輯塊,由于Fragments是建構在views之上的,而views很容易實作動畫效果,是以Fragments在螢幕切換時具有更好的控制。

說了半天的Fragment,也看到這麼多次Fragment這個名詞出現,那麼Fragment到底是什麼東東呢?定義又是如何?

Fragment也可以叫為“片段”,但我覺得“片段”中文叫法有點生硬,還是保持叫Fragment比較好,它可以表示Activity中的行為或使用者界面部分。我們可以在一個Activity中用多個Fragment組合來建構多窗格的UI,以及在多個Activity中重複使用某個Fragment。它有自己的生命周期,能接受自己的輸入,并且可以在 Activity 運作時添加或删除Fragment(有點像在不同 Activity 中重複使用的“子 Activity”)。

簡單來說,Fragment其實可以了解為一個具有自己生命周期的控件,隻不過這個控件又有點特殊,它有自己的處理輸入事件的能力,有自己的生命周期,又必須依賴于Activity,能互相通信和托管。

如圖:

對于Fragment的一些了解

這張圖是Fragment生命周期和Activity生命周期對比圖,可以看到兩者還是有很多相似的地方,比如都有onCreate(),onStart(),onPause(),onDestroy()等等,因為Fragment是被托管到Activity中的,是以多了兩個onAttach()和onDetach()。這裡講講與Activity生命周期不一樣的方法。

onAttach()

Fragment和Activity建立關聯的時候調用,被附加到Activity中去。

onCreate()

系統會在建立Fragment時調用此方法。可以初始化一段資源檔案等等。

onCreateView()

系統會在Fragment首次繪制其使用者界面時調用此方法。 要想為Fragment繪制 UI,從該方法中傳回的 View 必須是Fragment布局的根視圖。如果Fragment未提供 UI,您可以傳回 null。

onViewCreated()

在Fragment被繪制後,調用此方法,可以初始化控件資源。

onActivityCreated()

當onCreate(),onCreateView(),onViewCreated()方法執行完後調用,也就是Activity被渲染繪制出來後。

onPause()

系統将此方法作為使用者離開Fragment的第一個信号(但并不總是意味着此Fragment會被銷毀)進行調用。 通常可以在此方法内确認在目前使用者會話結束後仍然有效的任何更改(因為使用者可能不會傳回)。

onDestroyView()

Fragment中的布局被移除時調用。

onDetach()

Fragment和Activity解除關聯的時候調用。

但需要注一點是:除了onCreateView,其他的所有方法如果你重寫了,必須調用父類對于該方法的實作。

還有一般在啟動Fragment的時候,它的生命周期就會執行這幾個方法。

對于Fragment的一些了解

前面介紹了半天,不耐煩的人會說,這麼多廢話,也不見的到底是如何使用,畢竟我們是開發者,需要的使用方式,那麼現在就來說說用法如何吧。兩種方式:靜态用法和動态用法。

靜态用法

1、繼承Fragment,重寫onCreateView決定Fragemnt的布局

2、在Activity中聲明此Fragment,就當和普通的View一樣

首先是布局檔案:fragment1.xml

可以看到,這個布局檔案非常簡單,隻有一個LinearLayout,裡面加入了一個TextView。我們再建立一個fragment2.xml :

然後建立一個類Fragment1,這個類是繼承自Fragment的:

可以看到,在onCreateView()方法中加載了fragment1.xml的布局。同樣fragment2.xml也是一樣的做法,建立一個Fragment2類:

然後打開或建立activity_main.xml作為主Activity的布局檔案,在裡面加入兩個Fragment的引用,使用android:name字首來引用具體的Fragment:

最後建立MainActivity作為程式的主Activity,裡面的代碼非常簡單,都是自動生成的:

現在我們來運作一次程式,就會看到,一個Activity很融洽地包含了兩個Fragment,這兩個Fragment平分了整個螢幕,效果圖如下:

對于Fragment的一些了解

動态用法

上面僅僅是Fragment簡單用法,它真正強大部分是在動态地添加到Activity中,那麼動态用法又是如何呢?

還是在靜态用法代碼的基礎上修改,打開activity_main.xml,将其中對Fragment的引用都删除,隻保留最外層的LinearLayout,并給它添加一個id,因為我們要動态添加Fragment,不用在XML裡添加了,删除後代碼如下:

然後打開MainActivity,修改其中的代碼如下所示:

看到了沒,首先,我們要擷取螢幕的寬度和高度,然後進行判斷,如果螢幕寬度大于高度就添加fragment1,如果高度大于寬度就添加fragment2。動态添加Fragment主要分為4步:

1.擷取到FragmentManager,在Activity中可以直接通過getFragmentManager得到。

2.開啟一個事務,通過調用beginTransaction方法開啟。

3.向容器内加入Fragment,一般使用replace方法實作,需要傳入容器的id和Fragment的執行個體。

4.送出事務,調用commit方法送出。

現在運作一下程式,效果如下圖所示:

對于Fragment的一些了解
對于Fragment的一些了解

要想管理 Activity 中的片段,需要使用 FragmentManager。要想擷取它,需要 Activity 調用 getFragmentManager()。

使用 FragmentManager 執行的操作包括:

通過 findFragmentById()(對于在 Activity 布局中提供 UI 的片段)或 findFragmentByTag()(對于提供或不提供 UI 的片段)擷取 Activity 中存在的片段

通過 popBackStack()将片段從傳回棧中彈出

通過 addOnBackStackChangedListener() 注冊一個偵聽傳回棧變化的偵聽器

也可以使用 FragmentManager 打開一個 FragmentTransaction,通過它來執行某些事務,如添加和删除片段。

盡管 Fragment 是作為獨立于 Activity的對象實作,并且可在多個 Activity 内使用,但Fragment 的給定執行個體會直接綁定到包含它的 Activity。具體地說,Fragment 可以通過 getActivity() 通路 Activity執行個體,并輕松地執行在 Activity 布局中查找視圖等任務。如:

同樣地,Activity 也可以使用 findFragmentById() 或 findFragmentByTag(),通過從 FragmentManager 擷取對 Fragment 的引用來調用Fragment中的方法。例如:

建立對 Activity 的事件回調

在某些情況下,可能需要通過與 Activity 共享事件。執行此操作的一個好方法是,在Fragment 内定義一個回調接口,并要求宿主 Activity 實作它。 當 Activity 通過該接口收到回調時,可以根據需要與布局中的其他Fragment共享這些資訊。

例如,如果一個新聞應用的 Activity 有兩個Fragment ,一個用于顯示文章清單(Fragment A),另一個用于顯示文章(Fragment B)—,那麼Fragment A必須在清單項被標明後告知 Activity,以便它告知Fragment B 顯示該文章。 在本例中,OnArticleSelectedListener 接口在片段 A 内聲明:

然後,該Fragment的宿主 Activity 會實作 OnArticleSelectedListener 接口并替代 onArticleSelected(),将來自Fragment A 的事件通知Fragment B。為確定宿主 Activity 實作此界面,Fragment A 的 onAttach() 回調方法(系統在向 Activity 添加Fragment時調用的方法)會通過轉換傳遞到 onAttach() 中的 Activity 來執行個體化 OnArticleSelectedListener 的執行個體:

如果 Activity 未實作界面,則片段會引發 ClassCastException。實作時,mListener 成員會保留對 Activity 的 OnArticleSelectedListener 實作的引用,以便Fragment A 可以通過調用 OnArticleSelectedListener 界面定義的方法與 Activity 共享事件。例如,如果Fragment A 是 ListFragment 的一個擴充,則使用者每次點選清單項時,系統都會調用Fragment中的 onListItemClick(),然後該方法會調用 onArticleSelected() 以與 Activity 共享事件:

因為Fragment是由FragmentManager來管理,每一個Activity有一個FragmentManager,管理着一個Fragment的棧,Activity是系統級别的,由系統來管理ActivityManager,棧也是系統範圍的。而Fragment則是每個Activity範圍内的,是以在使用Fragment的時候也有幾點要注意。

同一個Activity中,隻能有一個ID或TAG辨別的Fragment執行個體。

這很容易了解,同一個範圍内,有辨別的執行個體肯定是要唯一才行(否則還要辨別幹嘛)這個在布局中經常犯錯,在布局中寫Fragment最好不要加ID或者TAG,否則很容易出現不允許建立的錯誤。我的原則是如果放在布局中,就不要加ID和TAG,如果需要ID和TAG就全用代碼控制。建立新執行個體前先到FragmentManager中查找一番,這也正是有辨別的意義所在。

一個Activity中有一個Fragment池,執行個體不一定會被銷毀,可能會儲存在池中。

這個跟第一點差不多。就好比系統會緩存Activity的執行個體一樣,FragmentManager也會緩存Fragment執行個體,以友善和加速再次顯示。

FragmentManager的作用範圍是整個Activity,是以,某一個布局ID,不能重複被Fragment替換。

通常顯示Fragment有二種方式,一種是層疊到某個布局上,或者把某個布局上面的Fragment替換掉,但是這個布局不能出現二次,比如布局A中有ID為id的區域,要顯示為Fragment,此布局A,隻能在一個Activity中顯示一個,否則第二個id區域不能被Fragment成功替換。因為雖有二個ID布局的執行個體,但ID是相同的,對FragmentManager來說是一樣的,它會認為隻有一個,因為它看的是布局的ID,而不是布局的執行個體。

Fragment的生命周期反應Activity的生命周期。

Fragment在顯示和退出時會走一遍完整的生命周期。此外,正在顯示時,就跟Activity的一樣,Activity被onPause,裡面的Fragment就onPause,以此類推,由此帶來的問題就是,比如你在onStart()裡面做了一些事情,那麼,當宿主Activity被擋住,又出現時(比如接了個電話),Fragment的onStart也會被高到,是以你要想到,這些生命周期不單單在顯示和退出時會走到。

Fragment的可見性。

這個問題出現在有Fragment棧的時候,也就是說每個Fragment不知道自己是否真的對使用者可見。比如現在是Fragment A,又在其上面顯示了Fragment B,當B顯示後,A并不知道自己上面還有一個,也不知道自己對使用者不可見了,同樣再有一個C,B也不知。C退出後,B依然不知自己已在棧頂,對使用者可見,B退後,A也不知。也就是說Fragment顯示或者退出,棧裡的其他Fragment無法感覺。這點就不如Activity,a被b蓋住後,a會走到onStop(),同樣c顯示後,b也能通過onStop()感覺。Fragment可以從FragmentManager監聽BackStackState的變化,但它隻告訴你Stack變了,不告訴你是多了,還是少,還有你處的位置。有一個解決方案就是,記錄頁面的Path深度,再跟Fragment所在的Stack深度來比較,如果一緻,那麼這個Fragment就在棧頂。因為每個頁面的Path深度是固定的,而Stack深度是不變化的,是以這個能準确的判斷Fragment是否對使用者可見,當然,這個僅針對整個頁面有效,對于布局中的一個區域是無效的。

Fragment的事件傳遞。

對于層疊的Fragment,其實就相當于在一個FrameLayout裡面加上一堆的View,是以,如果處于頂層的Fragment沒處理點選事件,那麼事件就會向下層傳遞,直到事件被處理。比如有二個Fragment A和B,B在A上面,B隻有TextView且沒處理事件,那麼點選B時,會發現A裡的View處理了事件。這個對于Activity也不會發生,因為事件不能跨窗體傳播,上面的Activity沒處理事件,也不會傳給下面的Activity,即使它可見。解決之法,就是讓上面的Fragment的根布局吃掉事件,為每個根ViewGroup添加onClick=“true”。

與第三方Activity互動。與第三方互動,仍要采用Android的标準startActivityForResult()和onActivityResult()這二個方法來進行。但對于Fragment有些事情需要注意,Fragment也有這二個方法,但是為了能正确的讓Fragment收到onActivityResult(),需要:

宿主Activity要實作一個空的onActivityResult(),裡面調用super.onActivityResult()

調用Fragment#startActivityForResult()而不是用Activity的 當然,也可以直接使用Activity的startActivityForResult(),那樣的話,就隻能在宿主Activity裡處理傳回的結果了。

在用法的代碼部分參考郭神的部落格,感覺郭神在代碼講解部分通俗易懂,看起來也友善。總之,在使用Fragment也有一些注意事項,不是那麼完美的,雖然谷歌推薦我們用Fragment來代替Activity來使用,我們也确實這做了,現在基本主流的APP也都是少量Activity+很多Fragment,但也需要避免有些坑慎入。

1,https://developer.android.com/guide/components/fragments.html

2,http://blog.csdn.net/guolin_blog/article/details/8881711

3,http://toughcoder.net/blog/2014/10/22/effective-android-ui-architecture

源于對掌握的Android開發基礎點進行整理,羅列下已經總結的文章,從中可以看到技術積累的過程。

1,Android系統簡介

2,ProGuard代碼混淆

3,講講Handler+Looper+MessageQueue關系

4,Android圖檔加載庫了解

5,談談Android運作時權限了解

6,EventBus初了解

7,Android 常見工具類

8,對于Fragment的一些了解

9,Android 四大元件之 " Activity "

10,Android 四大元件之" Service "

11,Android 四大元件之“ BroadcastReceiver "

12,Android 四大元件之" ContentProvider "

13,講講 Android 事件攔截機制

14,Android 動畫的了解

15,Android 生命周期和啟動模式

16,Android IPC 機制

17,View 的事件體系

18,View 的工作原理

19,了解 Window 和 WindowManager

20,Activity 啟動過程分析

21,Service 啟動過程分析

22,Android 性能優化

23,Android 消息機制

24,Android Bitmap相關

25,Android 線程和線程池

26,Android 中的 Drawable 和動畫

27,RecylerView 中的裝飾者模式

28,Android 觸摸事件機制

29,Android 事件機制應用

30,Cordova 架構的一些了解

31,有關 Android 插件化思考

32,開發人員必備技能——單元測試