內容概要
* 這是我的第二篇博客,但爲什麼是android學習(3)呢,因爲我的第二篇博客的代碼不在這裏,只好先來寫這一篇了。
* 今天上午把android英文文檔的Fragment部分閱讀完畢,那是相當的吃力,還好有個中文的可以對照着看,這篇主要來記錄我學習Android Fragment時所經歷的一些代碼與疑惑等;
基本概念
Fragment 表示 Activity 中的行爲或用戶界面部分。您可以將多個片段組合在一個 Activity 中來構建多窗格 UI,以及在多個 Activity 中重複使用某個片段。您可以將片段視爲 Activity 的模塊化組成部分,它具有自己的生命週期,能接收自己的輸入事件,並且您可以在 Activity 運行時添加或刪除片段(有點像您可以在不同 Activity 中重複使用的“子 Activity”)。
其原理圖,一目瞭然:
按照我的理解這也是模塊化的一種多樣性的展現,就像造零件,一個零件可以作爲一個東西,這一個零件和其他零件組合到一起,又成爲一個大的零件,我這麼說好粗鄙啊,,,我隱隱中覺得這個和material design有些許的聯繫,但是又覺得相去甚遠,還需學習下material design才能再做打算;
主要知識點
(1)Fragment子類
- DialogFragment
- ListFragment
- PreferenceFragment
(2)使用的基本流程
- 添加用戶界面
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);
}
}
爲了給fragment提供一個佈局,你必須實現onCreateView()回調函數,在繪製fragment佈局時Android系統會調用它。實現這個函數時需要返回fragment所屬的根View。
注意:如果你的fragment時ListFragment的子類,默認實現從onCreateView()返回一個ListView,所以你不需要實現它。
爲了從onCreateView()返回一個佈局,你可以從layout resource定義的XML文件inflate它。爲了便於你這樣做,onCreateView()提供一個LayoutInflater對象。inflate()函數需要以下三個參數:
要inflate的佈局的資源ID。
被inflate的佈局的父ViewGroup。傳入container很重要,這是爲了讓系統將佈局參數應用到被inflate的佈局的根view中去,由其將要嵌入的父view指定。
一個布爾值,表明在inflate期間被infalte的佈局是否應該附上ViewGroup(第二個參數)。(在這個例子中傳入的是false,因爲系統已經將被inflate的佈局插入到容器中(container)——傳入true會在最終的佈局裏創建一個多餘的ViewGroup。)
- 將fragment添加到activity之中
兩種方法:
1.佈局文件聲明,主要屬性:name、id;
2.通過編碼將fragment添加到已存在的ViewGroup中;
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
Fragment事務管理
想要管理activity中的fragment,可以使用FragmentManager。可以通過在activity中調用getFragmentManager()獲得。
使用FragmentManager 可以做如下事情,包括:
使用findFragmentById()(用於在activity佈局中提供有界面的fragment)或者findFragmentByTag()獲取activity中存在的fragment(用於有界面或者沒有界面的fragment)。
使用popBackStack()(模仿用戶的BACK命令)從後臺棧彈出fragment。
使用addOnBackStackChangedListener()註冊一個監聽後臺棧變化的監聽器。addToBackStack
在調用commit()之前,爲了將事務添加到fragment事務後臺棧中,你可能會想調用addToBackStatck()。這個後臺棧由activity管理,並且允許用戶通過按BACK鍵回退到前一個fragment狀態。
代碼示例:
// Create new fragment and transaction
Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
- 與Activity交互
(1)fragment訪問activity:
View listView = getActivity().findViewById(R.id.list);
(2)activity訪問fragment:
ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);
(3)fragment與activity共享事件
在一些情況下,你可能需要fragment與activity共享事件。這樣做的一個好方法是在fragment內部定義一個回調接口,並需要宿主activity實現它。當activity通過接口接收到回調時,可以在必要時與佈局中的其它fragment共享信息。
舉個例子,如果新聞應用的actvity中有兩個fragment——一個顯示文章列表(fragment A),另一個顯示一篇文章(fragment B)——然後fragment A 必須要告訴activity列表項何時被選種,這樣,activity可以通知fragment B顯示這篇文章。這種情況下,在fragment A內部聲明接口OnArticleSelectedListener:
public static class FragmentA extends ListFragment {
...
// Container Activity must implement this interface
public interface OnArticleSelectedListener {
public void onArticleSelected(Uri articleUri);
}
...
}
然後fragment的宿主activity實現了OnArticleSelectedListener接口,並且重寫onArticleSelected()以通知fragment B來自於fragment A的事件。爲了確保宿主activity實現了這個接口,fragment A的onAttach()回調函數(當添加fragment到activity中時系統會調用它)通過作爲參數傳入onAttach()的activity的類型轉換來實例化一個OnArticleSelectedListener實例。
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");
}
}
...
}
如果activity沒有實現這個接口,那麼fragment會拋出一個ClassCaseException異常。一旦成功,mListener成員會保留一個activity的OnArticleSelectedListener實現的引用,由此fragment A可以通過調用由OnArticleSelectedListener接口定義的方法與activity共享事件。例如,如果fragment A是ListFragment的子類,每次用戶點擊列表項時,系統都會調用fragment的onListItemClick()事件,然後fragment調用onArticleSelected()來與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);
}
...
}
傳遞給onListItemClick()的參數id是點擊的列表項行id,activity(或者其它fragment)用以從應用的ContentProvider獲取文章。
以上具體參見:android文檔-fragment
DEMO
這個demo是按照文檔的最後一個例子來的,這個例子的源碼我找了好久,就是找不到,然後在StackOverFlow上搜到了一個問題,他也找不到,哈哈哈:
android-fragment-source-code-for-fragmentlayout-java
然後下載了Level18的Sample,在裏面找到了相應的源碼,提取出來在Android Studio中復原了這個莎士比亞的戲劇閱讀小程序:
這個小程序主要是根據手機的橫豎屏來判斷Layout文件的加載,橫屏加載兩個Fragment,豎屏只加載一個,具體的見源碼吧,我還沒有完全的理解透徹,感覺官網給的代碼要嚴謹好多;
源碼如下:
百度網盤-FragmentDemo
最後上幾張圖吧,無圖無真相: