Android3.0(API11)開始引入Fragment,主要是爲了在大屏幕上更好的支持動態和靈活的UI設計。以平板爲例,有足夠大的空間容納更多的組件,管理起來無疑過於麻煩。Fragment能夠分割Activity的layout,而且具備本身的生命週期,從而使得組件獨立管理成爲可能。正由於fragment具備獨立的生命週期,因此能夠定義本身的layout和行爲,從而一個fragment能夠在多個activity中重複調用。因此咱們在設計fragment的時候,要儘可能追求模塊化和可重用性。通常在平板和TV上廣泛使用。android
舉例以下:在平板上,activity使用一個fragment展現文章列表,使用另外一個fragment展現文章內容;在手機上,activity使用一個fragment展現文章列表,點擊跳轉到另外一個activity展現。bash
fragment的生命週期和activity很是相似,都有onCreate()、onStart()、onStop()、onPause()回調方法。fragment通常至少要實現三個回調方法:app
常見的有三種fragment,分別是DialogFragment、ListFragment、PreferenceFragmentide
fragment通常具備本身的UI,併成爲activity UI的一部分。爲了給fragment建立UI,必須實現onCreateView()回調方法,並返回一個view(fragment的根layout)。模塊化
若是fragment是ListFragment的子類,onCreateView()默認返回ListView,不須要重寫。佈局
public static class ExampleFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.example_fragment, container, false);
}
}複製代碼
container是activity的layout,fragment的layout將會被插入其中;savedInstanceState即保存的fragment被異常銷燬前的狀態;inflate()有三個參數:ui
通常來講,fragment的layout會被嵌入成爲activity view層級的一部分。有兩種方式能夠把fragment添加到activity的layout中:1.靜態加載;2.動態加載。this
靜態加載:在activity layout中聲明fragment,代碼以下:spa
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="com.example.chaos.myapplication.MainActivity"
tools:showIn="@layout/activity_main">
<fragment
android:id="@+id/fragment_person_list"
android:name="com.example.chaos.myapplication.PersonListFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<fragment
android:id="@+id/fragment_person_detail"
android:name="com.example.chaos.myapplication.DetailFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>複製代碼
系統建立該activity的layout時,會將指定的fragment所有實例化並分別調用他們的onCreateView()回調。而後檢索fragment的layout,並將onCreateView()返回的view取代<fragment>
元素直接插入activity的layout中。線程
activity重啓時,須要恢復fragment的數據,這就要求fragment要有個惟一的標識符(不然沒法管理fragment)。有三種方式能夠爲fragment賦予惟一的id
- 使用android:id屬性賦予id
- 使用android:tag屬性賦予惟一標籤
- 若是id和tag都沒提供的話,系統模式使用container view的id
2.. 動態加載:動態添加fragment到ViewGroup中
只要在activity在運行,均可以添加fragment到activity的layout中,只須要指定ViewGroup存放fragment就行。
爲了在activity中操做fragment的事務(add、remove、replace),必須使用FragmentTransaction()。代碼以下:
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();複製代碼
添加fragment使用add()方法,指定ViewGroup和要添加的fragment,對fragmentTransaction作的任何改動都要執行commit()方法才能生效。
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();複製代碼
並不是全部fragment都必須有UI,fragment也能夠用來執行後臺操做,此時,無需UI也能夠。該如何添加一個沒有UI的fragment呢?
首先,須要先經過findFragmentByTag()找到,由於tag是惟一能標識它的(只能經過add(fragment,string)主動設置tag)。
若是findFragmentByTag()返回null的話,須要經過add(fragment,string)爲fragment主動設置tag並添加。
fragment的管理是由FragmentManager實現的,能夠在activity中經過getFragmentManager()獲取FragmentManager。FragmentManager的功能以下:
靜態加載的fragment必定具備id或tag;動態加載的id必定具備container view的id或tag(fragment若是沒有惟一標識符,則沒法進行有效管理),動態加載一共有三個方法:
add(Fragment fragment, String tag)
:適用於有UI和無UI的fragmentadd(@IdRes int containerViewId, Fragment fragment)
:適用於有UI的fragmentadd(@IdRes int containerViewId, Fragment fragment,String tag);
:適用於有UI的fragment因此沒有UI的fragment,只能經過findFragmentByTag()獲取;有UI的fragment能夠經過id或tag獲取。
也可使用FragmentManager打開FragmentTransaction,從而操做fragment的事務,如添加或移除。
一個事務就是一系列變化,事務具備原子性、一致性、隔離性、持久性。可使用FragmentTransaction實現事務,也能夠把事務放到由activity管理的返回棧中,便於用戶返回以前的fragment的狀態.
能夠從FragmentManager獲取FragmentTransaction實例
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();複製代碼
FragmentTransactioni能夠藉由add()、remove()、replace()實現對事務的操做,並且每次操做必須實現commit()才能生效。
若是想把fragment加入到返回棧,以便用戶退回到fragment原先的狀態的話,須要在commit()以前調用addBackStack()方法:
DetailFragment detailFragment = new DetailFragment();
fragmentTransaction.add(detailFragment,"");
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();複製代碼
爲了檢索返回棧中的fragment,必須監聽onBackPressed()事件,不然的話若是棧中存在其餘activity的話,就會直接返回到其餘activity中;若是沒有activity的話,就會直接退出應用。
@Override
public void onBackPressed() {
if (getFragmentManager().getBackStackEntryCount() > 0){
getFragmentManager().popBackStack();
}else {
super.onBackPressed();
}
}複製代碼
添加改變到Transaction中的順序可有可無,除非:
若是沒有調用addBackStack(),當remove一個fragment時,commit以後會馬上銷燬,用戶沒法返回;若是添加到返回棧中,remove一個fragment時,commit以後會處於stop狀態,用戶能夠返回到以前的狀態(resume)
commit()這個操做運行在主線程中,也就意味着調用commit()以後要排序執行,並不會馬上執行事務。若是想馬上執行的話,能夠在commit()以前調用excutePendingTransactions(),不過通常並沒有此必要。
commit()必須在onSaveInstanceState()以前調用,由於在此以後,activity不會保存數據,致使commit()調用會拋出異常;若是容許狀態數據丟失的話,能夠調用 commitAllowingStateLoss()。
雖然fragment做爲對象的實現和activity存在很大不一樣,可是做爲activity的一部分,fragment又和activity緊密聯繫。
View listView = getActivity().findViewById(R.id.list);
DetailFragment detailFragment1 = (DetailFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_person_detail);複製代碼
fragment建立接口和方法:
public static class FragmentA extends ListFragment {
...
// Container Activity must implement this interface
public interface OnArticleSelectedListener {
public void onArticleSelected(Uri articleUri);
}
...
}複製代碼
把activity轉換成接口,從而實現activity監聽
public static class FragmentA extends ListFragment {
OnArticleSelectedListener mListener;
...
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (OnArticleSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");
}
}
...
}複製代碼
在fragment的onListItemClick事件中調用傳值方法給activity
public static class FragmentA extends ListFragment {
OnArticleSelectedListener mListener;
...
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Append the clicked item's row ID with the content provider Uri Uri noteUri = ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id); // Send the event and Uri to the host activity mListener.onArticleSelected(noteUri); } ... }複製代碼
和activity同樣,fragment也有三種基本狀態:
fragment和activity生命週期的一個最大差異在於兩者在返回棧中的存儲。activity的返回棧是由系統管理的;fragment的返回棧是由activity管理的(當fragment被remove時,調用addBackStack())。
fragment若是須要context對象的話,能夠調用getActivity(),可是這個方法要在onAttach()以後、onDetach()以前調用纔有效,不然將會返回null。