安卓自3.0開始引入Fragment的概念,主要是為了能在不同分辯率屏幕上進行更為動態和靈活的UI設計,讓程序更加合理和充分利用大屏幕空間。本篇將學習Fragment以下幾個知識點:
- Fragment概要
- Fragment生命周期
- 加載Fragment方法
- 靜態加載
- 動態加載
- Fragment與Activity之間通信
1.Fragment概要
學習Fragment的時候可以聯系之前學習過的Activity,因為它們有很多相似點:都可包含布局,有自己的生命周期,Fragment可看似迷你活動。正如Fragment的名字--碎片,它的出現是為了解決Android碎片化 ,它可作為Activity界面的組成部分,可在Activity運行中實現動態地加入、移除和交換。一個Activity中可同時出現多個Fragment,一個Fragment也可在多個Activity中使用。活動和碎片像極了夫妻, 雖然緊密聯系但是又有獨立空間,在一起讓彼此變得更好。
下面這個非常經典的例子更直觀地說明了Fragment作用:
2.Fragment生命周期
先來看官方文檔提供的有關Fragment生命周期的圖片。
是不是能發現Fragment和Activity的生命周期太相似了,現在只需要再介紹幾個Activity中沒講過的新方法:
onAttach():當Fragment和Activity建立關聯時調用
onCreateView():當Fragment創建視圖時調用
onActivityCreated():當與Fragment相關聯的Activity完成onCreate()之后調用
onDestroyView():在Fragment中的布局被移除時調用
onDetach():當Fragment和Activity解除關聯時調用
在上圖中畫了幾條線,可以看到Fragment周期中的狀態幾乎都是成對出現的,所以不難理解下圖幾種變化下Fragment生命周期方法的調用順序了。
加載到Activity中的Fragment在各種變化下方法的調用順序更值得注意。需要提一句的是,Activity的FragmentManager負責調用隊列中Fragment的生命周期方法,只要Fragment的狀態與Activity的狀態保持了同步,托管Activity的FragmentManager便會繼續調用其他生命周期方法以繼續保持Fragment與Activity的狀態一致。
Fragment生命周期與Activity生命周期的一個關鍵區別就在于,Fragment的生命周期方法是由托管Activity而不是操作系統調用的。Activity中生命周期方法都是protected,而Fragment都是public,也能印證了這一點,因為Activity需要調用Fragment那些方法并管理它。
3.加載Fragment方法
現在就來學習如何在Activity中加載Fragment。
(1)靜態加載:在托管Activity的layout文件中聲明Fragment
靜態加載Fragment大致過程如圖,分成四步:
下面通過一個簡單的例子感受Fragment靜態加載到Activity的過程。
第一步:新建frag_layout.xml,為Fragment指定一個布局,這里簡單的放一個TextView和一個Button。
第二步:新建一個MyFragment類并繼承Fragment,這里引用android.app包下的就可以,另一個包下主要用于兼容低版本的安卓系統。然后重寫onCreateView()方法,這個方法里通過LayoutInflater的inflate()方法將剛剛定義的frag_layout布局加載進來并得到一個View,再return這個View。
第三步:新建mian.xml,作為Activity的布局,使用< fragment>標簽添加碎片,并且一定要有android:name屬性且值為被加載的Fragment類的包名.類名完整形式。
第四步:在MainActivity中加載main布局。現在MyFragment就完成了靜態加載到MainActivity中,這時碎片里的控件自然也是活動的一個部分,可直接在活動中獲取到Button的實例,來注冊點擊事件了。
運行一下看看能不能達到效果:
(2)動態加載:在托管Activity通過代碼動態添加
動態加載的代碼也非常簡單,直接看例子。修改main.xml,將整個< fragment>刪掉。但還保留一個LinerLayout的空間并且還給了Id,為何這樣做?馬上揭秘。
接下來在MainActivity中添加幾行代碼:
可將整個過程大致分為三個步驟:
第一步,先用getFragmentManager()方法獲取一個FragmentManager對象,再通過它的beginTransaction()獲取一個FragmentTransaction的實例。
第二步,用beginTransaction.add()方法將MyFragemnt實例添加到main布局里LinearLayout里,終于知道之前鋪墊的Id是怎么回事了。一定要注意,add()方法里的第一個參數是容器視圖資源Id,而不是layout。容器視圖資源Id有兩個作用:告知FragmentManager,碎片視圖應該出現在活動視圖的什么地方;它也是FragmentManager隊列中碎片的唯一標識符。而靜態加載時碎片的唯一標識符正是在活動布局里< fragment>下的id。
第三步:調用beginTransaction.commit()提交。另外,如果允許用戶通過按下返回按鍵返回到前一個Fragment狀態,在調用commit()之前先調用addToBackStack(true)方法。
這里注意到動態加載進來的Fragment里的控件并不能直接在活動中findViewById得到,那么如何實現點擊效果呢,學完下一個知識點就有辦法了。
4.Fragment與Activity之間通信
在活動中可以通過調用FragmentManager的findFragmentById()方法來得到相應碎片的實例,繼而可以調用碎片里的方法。以上面demo舉例,如果想得到靜態加載碎片的實例,可在MainActivity添加代碼如下:
MyFragment myFragment = (MyFragment)getFragmentManager(). findFragmentById(R.id.fragment);
如果想得到動態加載碎片的實例,代碼如下:
MyFragment myFragment = (MyFragment)fragmentManager(). findFragmentById(R.id.layout);
在碎片中也可以通過調用getActivity()方法來得到和當前碎片相關聯的活動實例,這樣調用活動里的方法就變得輕而易舉了。比如想在MyFragment得到MainActivity的實例:
MainActivity activity=(MainActivity)getActivity();
于是碎片和活動可以很方便地進行通信了。再想一想碎片和碎片之間如何進行通信?先在一個碎片中可以得到與它相關聯的活動,然后再通過這個活動去獲取另外一個碎片的實例,這樣實現了不同碎片之間的通信了。
現在你有沒有想到解決之前那個問題的辦法呢?可以這樣做,修改MyFragment,代碼如下圖所示:
現在按鈕點擊就又有響應了!其實在實際開發中,如果某一板塊每次用到都需要相同的功能,就完全可以在碎片中實現需求,而不必在活動中重復代碼,這樣可以提高代碼的復用性。
> 下一篇預告:數據存儲篇