Fragment在一個Activity中表示一個行爲或用戶界面的一部分,可以在一個Activity中組合多個Fragment形成一個多頁的用戶界面或者在多個Activity中重複使用一個Fragment,可以認爲Fragment是一個擁有獨立生命週期,獨立接收輸入事件,並且可以在Activity運行時添加或刪除的模塊化部分(有點像”內嵌的Activity“),Fragment不能單獨存在:必須依附於Activity並且其生命週期直接受Activity的生命週期影響,通過閱讀這一節的內容,將會瞭解到:
Fragment的使用場景
將一個Activity分成不同的部分,例如分成導航欄和內容塊
同一個界面模塊可以重複使用時,可以使用Fragment,這樣一個Fragment可以在多個Activity中使用,避免在一個Fragment中直接操作另外一個Fragment
Fragment的常用類型
DialogFragment:顯示一個浮動的Dialog,這和使用DialogHelper來顯示一個Dialog都是可以的,因爲你可以把Fragment對話框包含在Activity管理的回退棧中,允許用戶返回到關閉的Fragment中(原文,不太理解)
ListFragment:顯示一個有Adapter管理的List,和ListActivity相似
- PreferenceFragment:用一個List來顯示一組Preference對象,類似PreferenceActivity,在創建一個應用程序的設置界面時可能會用到
創建一個Fragment:(Fragment也有生命週期回調方法,比如onCreate(),onStart(),etc)
一個Fragment至少包含如下三個方法:
onCreate():當Fragment創建時調用,應該初始化一些你想在Fragment暫停或停止時保留的組件
onCreateView():當Fragment第一次繪製UI界面的時候調用,提供一個Layout佈局,對佈局中的組件的操作就在這裏進行(按鈕的點擊事件等),注意如果Fragment的類型是ListFragment,將會返回一個ListView而不是View
- onPause():當用戶離開當前Fragment時調用,不一定會被銷燬,但仍有必要在這裏做一些保存持久化數據的操作
更多關於Fragment中的回調方法寫在下面
將Fragment添加到Activity中的兩種方法:
在佈局文件中申明Fragment:首先創建一個自定義的Fragment類,這個類中的onCreateView()必須要返回一個View,也就是說必須爲它創建一個對應的佈局文件,然後在其他佈局中將這個Fragment作爲一個自定義控件來使用即可,注意,如果有多個Fragment,必須爲每一個Fragment提供一個唯一的標識符,如id屬性,tag屬性
在代碼中添加Fragment:在Activity運行的任何時候都可以動態的添加Fragment,只需要指定一個放置Fragment的ViewGroup(例如LinearLayout,FrameLayout等),Activity對Fragment的操作包括添加(add),移除(remove),替換(replace)
使用FragmentTransaction接口實現這些功能。(當然,這裏說的Fragment也是指已經創建好的Fragment類對象並在其onCreateView()方法中指定了佈局)文檔中下一節講的是沒有UI界面的Fragment,我認爲暫時不需要用到,所以就不記錄,先跳過
管理一些Fragments(通過FragmentManager類):
通過findFragmentById()方法獲取一個Activity中的Fragment實例
對Fragment回退棧進行操作,取出或者壓入等
- 註冊監聽事件來監聽回退棧的改變,例如addOnBackStackChangedListener()
需要注意一個關於導包的問題:如果導入的是android.app.Fragment,則使用的是:
FragmentManager fm = getFragmentManager();
而如果導入的是android.support.v4.app.Fragment,則應該使用:
FragmentManager fm = getSupportFragmentManager()
操作一些Fragments(通過FragmentTransaction類,主要有add(),remove(),place()等操作):
通過FragmentManager獲取FragmentTransaction的實例,接着就能對一些Fragments進行add(),remove(),或place()操作了,最後再執行commit()以提交事務,在執行commit()之前如果執行了addToBackStack()方法,系統將會將執行的這個事務(transaction)存放在回退找中,如果之前有多個操作,比如remove()了多個Fragment,最後再執行commit(),則在返回時(按下返回鍵)所有這些Fragment都將返回
也就是說,commit()相當於一個節點,你在這個節點之前的操作爲一個集合,執行返回的時候這個集合中的Fragment都將返回
比如一個頁面有3個Fragment,還有一個按鈕,點擊事件是將這三個Fragments移除(remove),然後調用addToBackStack(),將它們添加到回退找,然後提交(commit),這樣,在點擊按鈕的時候,這3個fragments都將消失,但是當我點擊物理返回鍵的時候,這3個Fragments都將重新出現在屏幕上,而如果沒有執行addToBackStack()方法,則不會返回之前的Fragment。調用commit()方法並不會立即執行事務(transaction),而是會等待UI線程的調度,待UI線程有空閒來執行時纔會執行。如果想要讓它立即執行,需要在UI線程中使用executePendingTransactions()方法來執行commit提交的事務,但一般沒必要這樣做,除非這個事務是在其他線程中產生的,需要立即執行,注意:只能在用戶離開當前Activity之前使用commit()方法來提交事務,也就是說必須在系統調用Activity的onSavaInstanceState()之前提交事務
Fragment與Activity之前的通信
由於Fragment總是依附於Activity的,所以很多時候需要Fragment和Activity之間進行數據傳遞等通信,下面是一個簡單的例子,用以說明Fragment與Activity之前如何通信:
假設一個Activity中有兩個Fragment(Fragment_A和Fragment_B),Fragment_A中是一個列表(list),Fragment_B中是列表單個項目的詳情頁,也就是說,點擊Fragment_A中的某個項目,就會跳轉到Fragment_B中顯示它的詳情,這時就需要通過Activity監聽用戶對Fragment_A的點擊事件,獲取到用戶點擊的項目索引,然後再傳遞給Fragment_B,這樣Fragment_B才知道該顯示哪個項目的詳情,下面是代碼:
- 首先在Fragment_A中定義一個接口 OnItemSelectListener,如下:
public class Fragment_A extends ListFragment {
public interface OnItemSelectListener {
public void onItemSelected(int itemId);
}
//其他方法
}
- 接着在其對應的Activity中實現這個接口並重寫其中的回調方法onItemSelected(),在這個回調方法中通知Fragment_B去顯示對應項目的詳情
public class MainActivity extends AppCompatActivity implements Fragment_A.OnItemSelectListener {
//實現接口中的方法
@Override
public void onItemSelected(int itemId) {
//這裏的Fragment_B是已經寫好的一個自定義Fragment類
Fragment fmB = new Fragment_B(itemId);
FragmentTransaction ft = getFragmentManager().beginTransaction();
//這裏的R.id.fm_b是在主佈局中(activity_main.xml)中的一個佈局控件的id,用來放置Fragment
ft.replace(R.id.fm_b, fmB);
ft.commit();
}
//其他方法
}
- 然後在Fragment_A的onAttach()方法中(onAttach方法爲Fragment生命週期中首先被調用的方法,並且需要當前Activity實例作爲參數)實例化OnItemSelectListener接口,獲取到對應的Activity(因爲這個Activity之前實現了OnItemSelectListener接口)
public class Fragment_A extends ListFragment {
//實例化一個接口對象
public OnItemSelectListener listener;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
listener = (OnItemSelectListener) activity;
}
public interface OnItemSelectListener {
public void onItemSelected(int id);
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
//這裏的listener指向MainActivity,因此這是調用MainActivity中的onItemSelected()方法
listener.onItemSelected(id);
}
//其他方法
}
通過這樣的接口實現,在Fragment_A中獲取到Activity的實例,然後Fragment_A就可以在合適的時候調用Activity實例的onItemSelected()方法來達到回調的目的,這樣,一個簡單的通過回調實現的Fragment與Activity之間的通信就實現了
Fragment中處理菜單點擊事件
Fragment也可以像Activity那樣使用菜單,只需要在Fragment中實現onCreateOptionMenu()方法即可,這個操作和在Activity中是一樣的,注意,爲了讓Fragment接收到菜單的回調,需要在Fragment的onCreate()方法 中調用setHasOptionMenu(),否則Fragment不能接收到菜單的點擊回調,也就是點了菜單沒反應,
其實,點擊菜單選項這個過程,首先接收到回調的是Activity,如果Activity中沒有處理選項點擊事件的方法,纔會移交給Fragment
處理Fragment的生命週期
就像Activity的生命週期一樣,Fragment也存在這三種狀態:
Resumed(在運行的Activity中可見狀態),
Paused(其他Activity沒有完全擋住當前Activity,類似點擊分享按鈕彈出的一個只遮住一部分屏幕的Activity,後面那個Activity就是處於這種狀態),
- Stopped(當前Fragment不可見了,或者所在的Activity不可見了,或者當前Fragment被移除進入回退棧了,但是一個Stopped狀態的Fragment依然是活着的,所有的狀態都仍然存在,直到其對應的Activity被銷燬)
Fragment也具有狀態保存的功能,通過在Fragment的onCreate(),onCreateView()或者onActivityCreated()這三個系統回調方法中執行onSaveInstanceState()
Activity與Fragment的生命週期最主要的區別在於他們的回退棧:
Activity的回退棧由系統管理,當用戶點擊物理返回鍵時,當前Activity將由系統默認放置到回退棧中
Fragment的回退棧有其所在的Actiity管理,而且只有當明確執行了addToBackStack()方法後,當前Fragment在移除時纔會被放入回退棧,否則就銷燬。
Fragment的生命週期與Activity是同步的,也就是說,當一個Activity含有一個Fragment的情況下,當用戶退出當前應用,系統會先調用Fragment的onPause(),再調用Activity的onPause(),同理,其他幾個生命週期回調方法類似,但是因爲Fragment的生命週期比Activity多幾個獨有的方法:
onAttach():當Fragment關聯(依附)到一個Activity時調用,也就是在一個Activity中創建了一個Fragment,並調用了commit()之後
onCreateView():當創建Fragment的佈局時調用
onActivityCreateed():當對應的Activity中的onCreate()方法返回時(…這裏有點搞不明白了,Activity中的onCreate()怎麼返回?)
onDestroyView():當Fragment的佈局被移除時調用
- onDetach():當Fragment與Activity斷開聯繫時調用
下面兩張圖來自Android官方文檔,左圖爲Fragment的生命週期圖,右圖爲Activity的狀態與Fragment的生命週期對比圖
————————