android之Fragment(官網資料翻譯)
Fragment要點
- Fragment作為Activity介面的一部分組成出現
- 可以在一個Activity中同時出現多個Fragment,並且,一個Fragment亦可在多個Activity中使用。
- 在Activity執行過程中,可以新增、移除或者替換Fragment(add()、remove()、replace())
- Fragment可以響應自己的輸入事件,並且有自己的生命週期,當然,它們的生命週期直接被其所屬的宿主activity的生命週期影響。
設計哲學
Android在3.0中引入了fragments的概念,主要目的是用在大螢幕裝置上--例如平板電腦上,支援更加動態和靈活的UI設計。平板電腦的螢幕要比手機的大得多,有更多的空間來放更多的UI元件,並且這些元件之間會產生更多的互動。Fragment允許這樣的一種設計,而不需要你親自來管理 viewhierarchy的複雜變化。 通過將activity的佈局分散到fragment中, 你可以在執行時修改activity的外觀,並在由activity管理的back stack中儲存那些變化.(http://developer.android.com/guide/topics/fundamentals/fragments.html)
例如, 一個新聞應用可以在螢幕左側使用一個fragment來展示一個文章的列表,然後在螢幕右側使用另一個fragment來展示一篇文章--2個fragment並排顯示在相同的一個activity中,並且每一個fragment擁有它自己的一套生命週期回撥方法,並且處理它們自己的使用者輸入事件。 因此, 取代使用一個activity來選擇一篇文章而另一個activity來閱讀文章的方式,使用者可以在同一個activity中選擇一篇文章並且閱讀, 如圖所示:
fragment在你的應用中應當是一個模組化和可重用的元件.即,因為fragment定義了它自己的佈局, 以及通過使用它自己的生命週期回撥方法定義了它自己的行為,你可以將fragment包含到多個activity中. 這點特別重要, 因為這允許你將你的使用者體驗適配到不同的螢幕尺寸.舉個例子,你可能會僅當在螢幕尺寸足夠大時,在一個activity中包含多個fragment,並且,當不屬於這種情況時,會啟動另一個單獨的,使用不同fragment的activity.
繼續之前那個新聞的例子 -- 當執行在一個特別大的螢幕時(例如平板電腦),應用可以在Activity A中嵌入2個fragment。然而,在一個正常尺寸的螢幕(例如手機)上,沒有足夠的空間同時供2個fragment用, 因此, Activity A會僅包含文章列表的fragment, 而當使用者選擇一篇文章時, 它會啟動ActivityB,它包含閱讀文章的fragment.因此, 應用可以同時支援上圖中的2種設計模式。
建立Fragment
通常, 應當至少實現如下的生命週期方法:
- onCreate()
當建立fragment時, 系統呼叫該方法.
在實現程式碼中,應當初始化想要在fragment中保持的必要元件, 當fragment被暫停或者停止後可以恢復. - onCreateView()
fragment第一次繪製它的使用者介面的時候, 系統會呼叫此方法. 為了繪製fragment的UI,此方法必須返回一個View, 這個view是你的fragment佈局的根view. 如果fragment不提供UI, 可以返回null. - onPause()
使用者將要離開fragment時,系統呼叫這個方法作為第一個指示(然而它不總是意味著fragment將被銷燬.) 在當前使用者會話結束之前,通常應當在這裡提交任何應該持久化的變化(因為使用者有可能不會返回).
其生命週期圖如下:
大多數應用應當為每一個fragment實現至少這3個方法,但是還有一些其他回撥方法你也應當用來去處理fragment生命週期的各種階段.全部的生命週期回撥方法將會在後面章節 Handlingthe Fragment Lifecycle 中討論.
除了繼承基類 Fragment , 還有一些子類你可能會繼承:
- DialogFragment
顯示一個浮動的對話方塊.
用這個類來建立一個對話方塊,是使用在Activity類的對話方塊工具方法之外的一個好的選擇,
因為你可以將一個fragment對話方塊合併到activity管理的fragment back stack中,允許使用者返回到一個之前曾被摒棄的fragment. - ListFragment
顯示一個由一個adapter(例如 SimpleCursorAdapter)管理的專案的列表, 類似於ListActivity.
它提供一些方法來管理一個list view, 例如 onListItemClick()回撥來處理點選事件. - PreferenceFragment
顯示一個 Preference物件的層次結構的列表, 類似於PreferenceActivity.
這在為你的應用建立一個"設定"activity時有用處.
新增一個使用者介面
fragment通常用來作為一個activity的使用者介面的一部分,並將它的layout提供給activity.為了給一個fragment提供一 個layout,你必須實現 onCreateView()回撥方法, 當到了fragment繪製它自己的layout的時候,Android系統呼叫它.你的此方法的實現程式碼必須返回一個你的fragment的 layout的根view.
注意: 如果你的fragment是ListFragment的子類,它的預設實現是返回從onCreateView()返回一個ListView,所以一般情況下不必實現它.
從onCreateView()返回的View, 也可以從一個layout的xml資原始檔中讀取並生成. 為了幫助你這麼做, onCreateView() 提供了一個LayoutInflater 物件.
舉個例子, 這裡有一個Fragment的子類, 從檔案 example_fragment.xml 載入了一個layout:
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);
}
}
傳入onCreateView()的container引數是你的fragmentlayout將被插入的父ViewGroup(來自activity的layout) savedInstanceState 引數是一個Bundle, 如果fragment是被恢復的,它提供關於fragment的之前的例項的資料,
inflate() 方法有3個引數:
- 想要載入的layout的resource ID.
- 載入的layout的父ViewGroup.
傳入container是很重要的, 目的是為了讓系統接受所要載入的layout的根view的layout引數,
由它將掛靠的父view指定. - 布林值指示在載入期間, 展開的layout是否應當附著到ViewGroup (第二個引數).
(在這個例子中, 指定了false, 因為系統已經把展開的layout插入到container –傳入true會在最後的layout中建立一個多餘的view group.)
將fragment新增到activity
通常地, fragment為宿主activity提供UI的一部分, 被作為activity的整個viewhierarchy的一部分被嵌入. 有2種方法你可以新增一個fragment到activity layout:在activity的layout檔案中宣告fragment
在這種情況下,你可以像為View一樣, 為fragment指定layout屬性.例子是一個有2個fragment的activity的layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment android:name="com.example.news.ArticleListFragment"
android:id="@+id/list"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
<fragment android:name="com.example.news.ArticleReaderFragment"
android:id="@+id/viewer"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>
<fragment> 中的 android:name屬性指定了在layout中例項化的Fragment類.當系統建立這個activity layout時,它例項化每一個在layout中指定的fragment,並呼叫每一個上的onCreateView()方法,來獲取每一個 fragment的layout.系統將從fragment返回的 View直接插入到<fragment>元素所在的地方.
注意: 每一個fragment都需要一個唯一的標識,如果activity重啟,系統可以用來恢復fragment(並且你也可以用來捕獲fragment來處理事務,例如移除它.)
有3種方法來為一個fragment提供一個標識:
- 為 android:id 屬性提供一個唯一ID.
- 為 android:tag 屬性提供一個唯一字串.
- 如果以上2個你都沒有提供, 系統使用容器view的ID.
撰寫程式碼將fragment新增到一個已存在的ViewGroup.
當activity執行的任何時候, 都可以將fragment新增到activity layout.只需簡單的指定一個需要放置fragment的ViewGroup.為了在你的 activity中操作fragment事務(例如新增,移除,或代替一個fragment),必須使用來自FragmentTransaction 的API.可以按如下方法,從你的Activity取得一個 FragmentTransaction 的例項:
FragmentManager fragmentManager =getFragmentManager();
FragmentTransaction fragmentTransaction =fragmentManager.beginTransaction();
然後你可以使用 add() 方法新增一個fragment, 指定要新增的fragment和要插入的view.
ExampleFragment fragment = newExampleFragment();
fragmentTransaction.add(R.id.fragment_container,fragment);
fragmentTransaction.commit();
add()的第一個引數是fragment要放入的ViewGroup, 由resource ID指定,第二個引數是需要新增的fragment.一旦用FragmentTransaction做了改變,為了使改變生效,必須呼叫commit().
新增一個無UI的fragment
之前的例子展示了對UI的支援, 如何將一個fragment新增到activity.然而,也可以使用fragment來為activity提供後臺行為而不用展現額外的UI.要新增一個無UI的fragment, 需要從activity使用 add(Fragment, String)來新增fragment (為fragment提供一個唯一的字串"tag", 而不是一個view ID).這麼做新增了fragment,但因為它沒有關聯到一個activity layout中的一個view, 所以不會接收到onCreateView()呼叫.因此不必實現此方法.
為fragment提供一個字串tag並不是專門針對無UI的fragment的–也可以提供字串tag給有UI的fragment–但是如果fragment沒有UI,那麼這個tag是僅有的標識它的途徑.如果隨後你想從activity獲取這個fragment, 需要使用 findFragmentByTag().
管理Fragment
要在activity中管理fragment,需要使用FragmentManager. 通過呼叫activity的getFragmentManager()取得它的例項.
可以通過FragmentManager做一些事情, 包括:
- 使用findFragmentById()(用於在activity layout中提供一個UI的fragment)或findFragmentByTag()(適用於有或沒有UI的fragment)獲取activity中存在的fragment
- 將fragment從後臺堆疊中彈出, 使用 popBackStack() (模擬使用者按下BACK 命令).
- 使用addOnBackStackChangeListener()註冊一個監聽後臺堆疊變化的listener.
處理Fragment事務
關於在activity中使用fragment的很強的一個特性是:根據使用者的互動情況,對fragment進行新增,移除,替換,以及執行其他動作.提交給activity的每一套變化被稱為一個事務,可以使用在FragmentTransaction中的 API 處理.我們也可以儲存每一個事務到一個activity管理的backstack,允許使用者經由fragment的變化往回導航(類似於通過 activity往後導航).
從 FragmentManager 獲得一個FragmentTransaction例項 :
FragmentManager fragmentManager =getFragmentManager();
FragmentTransaction fragmentTransaction =fragmentManager.beginTransaction();
每一個事務都是同時要執行的一套變化.可以在一個給定的事務中設定你想執行的所有變化,使用諸如 add()、remove()和 replace().然後, 要給activity應用事務, 必須呼叫 commit().
在呼叫commit()之前, 你可能想呼叫 addToBackStack(),將事務新增到一個fragment事務的backstack. 這個back stack由activity管理, 並允許使用者通過按下 BACK按鍵返回到前一個fragment狀態.
舉個例子, 這裡是如何將一個fragment替換為另一個, 並在後臺堆疊中保留之前的狀態:
// Create new fragment and transaction
Fragment newFragment = newExampleFragment();
FragmentTransaction transaction =getFragmentManager().beginTransaction();
// Replace whatever is in thefragment_container view with this fragment,
// and add the transaction to the backstack
transaction.replace(R.id.fragment_container,newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
在這個例子中,newFragment替換了當前layout容器中的由R.id.fragment_container標識的fragment.通過呼叫 addToBackStack(), replace事務被儲存到back stack,因此使用者可以回退事務,並通過按下BACK按鍵帶回前一個fragment.
如果新增多個變化到事務(例如add()或remove())並呼叫addToBackStack(),然後在你呼叫commit()之前的所有應用的變化會被作為一個單個事務新增到後臺堆疊, BACK按鍵會將它們一起回退.
新增變化到 FragmentTransaction的順序不重要, 除以下例外:
- 必須最後呼叫 commit().
- 如果新增多個fragment到同一個容器, 那麼新增的順序決定了它們在view hierarchy中顯示的順序.
提示: 對於每一個fragment事務, 你可以應用一個事務動畫,通過在提交事務之前呼叫setTransition()實現.
呼叫 commit() 並不立即執行事務.恰恰相反, 它將事務安排排期, 一旦準備好,就在activity的UI執行緒上執行(主執行緒).如果有必要, 無論如何, 你可以從你的UI執行緒呼叫executePendingTransactions()來立即執行由commit()提交的事務. 但這麼做通常不必要,除非事務是其他執行緒中的任務的一個從屬.
警告:你只能在activity儲存它的狀態(當使用者離開activity)之前使用commit()提交事務.
與Activity通訊
儘管Fragment被實現為一個獨立於Activity的物件,並且可以在多個activity中使用,但一個給定的fragment例項是直接繫結到包含它的activity的. 特別的,fragment可以使用 getActivity() 訪問Activity例項, 並且容易地執行比如在activity layout中查詢一個view的任務.
View listView =getActivity().findViewById(R.id.list);
同樣地,activity可以通過從FragmentManager獲得一個到Fragment的引用來呼叫fragment中的方法, 使用findFragmentById() 或 findFragmentByTag().
ExampleFragment fragment =(ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);
為Activity建立事件回撥方法
在一些情況下, 你可能需要一個fragment與activity分享事件. 一個好的方法是在fragment中定義一個回撥的interface, 並要求宿主activity實現它.當activity通過interface接收到一個回撥, 必要時它可以和在layout中的其他fragment分享資訊.
例如, 如果一個新的應用在activity中有2個fragment – 一個用來顯示文章列表(framgent A), 另一個顯示文章內容(fragment B) – 然後 framgent A必須告訴activity何時一個list item被選中,然後它可以告訴fragmentB去顯示文章.
在這個例子中, OnArticleSelectedListener 介面在fragment A中宣告:
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 implementOnArticleSelectedListener");
}
}
...
}
如果activity沒有實現介面, fragment會丟擲 ClassCastException 異常. 正常情形下,mListener成員會保持一個到activity的OnArticleSelectedListener實現的引用, 因此fragment A可以通過呼叫在OnArticleSelectedListener介面中定義的方法分享事件給activity.例如, 如果fragment A是一個 ListFragment的子類, 每次使用者點選一個列表項, 系統呼叫在fragment中的onListItemClick(),然後後者呼叫 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 獲取文章.
新增專案到ActionBar
你的fragment可以通過實現 onCreateOptionMenu() 提供選單項給activity的選項選單(以此類推, Action Bar也一樣).為了使這個方法接收呼叫,無論如何, 你必須在 onCreate() 期間呼叫 setHasOptionsMenu() 來指出fragment願意新增item到選項選單(否則, fragment將接收不到對 onCreateOptionsMenu()的呼叫).
隨後從fragment新增到Option選單的任何項,都會被追加到現有選單項的後面.當一個選單項被選擇, fragment也會接收到 對 onOptionsItemSelected() 的回撥.也可以在你的fragment layout中通過呼叫registerForContextMenu() 註冊一個view來提供一個環境選單.當使用者開啟環境選單, fragment接收到一個對 onCreateContextMenu() 的呼叫.當使用者選擇一個專案, fragment接收到一個對onContextItemSelected() 的呼叫.
注意: 儘管你的fragment會接收到它所新增的每一個選單項被選擇後的回撥, 但實際上當使用者選擇一個選單項時, activity會首先接收到對應的回撥.如果activity的on-item-selected回撥函式實現並沒有處理被選中的專案, 然後事件才會被傳遞到fragment的回撥.
這個規則適用於選項選單和環境選單.
處理fragment的生命週期
管理fragment的生命週期, 大多數地方和管理activity生命週期很像.和activity一樣, fragment可以處於3種狀態:
Resumed
在執行中的activity中fragment可見.
Paused
另一個activity處於前臺並擁有焦點, 但是這個fragment所在的activity仍然可見(前臺activity區域性透明或者沒有覆蓋整 個螢幕).
Stopped
要麼是宿主activity已經被停止, 要麼是fragment從activity被移除但被新增到後臺堆疊中.
停止狀態的fragment仍然活著(所有狀態和成員資訊被系統保持著). 然而, 它對使用者不再可見, 並且如果activity被幹掉, 他也會被幹掉.
其對應關係圖如下:
和activity一樣, 你可以使用Bundle保持fragment的狀態, 萬一activity的程式被幹掉,並且當activity被重新建立的時候, 你需要恢復fragment的狀態時就可以用到. 你可以在fragment的 onSaveInstanceState() 期間儲存狀態, 並可以在 onCreate(), onCreateView() 或 onActivityCreated() 期間恢復它.
生命週期方面activity和fragment之間最重要的區別是各自如何在它的後臺堆疊中儲存. 在預設情況下, activity在停止後, 它會被放到一個由系統管理的用於儲存activity的後臺堆疊.(因此使用者可以使用BACK按鍵導航回退到它).
然而, 僅當你在一個事務期間移除fragment時,顯式呼叫addToBackStack()請求儲存例項時,才被放到一個由宿主activity管理的後臺堆疊.
另外, 管理fragment的生命週期和管理activity生命週期非常類似.因此, "managing the activitylifecycle"中的相同實踐也同樣適用於fragment. 你需要理解的是, activity的生命如何影響fragment的生命.
與activity生命週期的協調工作
fragment所生存的activity的生命週期,直接影響fragment的生命週期,每一個activity的生命週期的回撥行為都會引起每一個fragment中類似的回撥.例如,當activity接收到onPause()時,activity中的每一個fragment都會接收到onPause().
Fragment 有一些額外的生命週期回撥方法, 那些是處理與activity的唯一的互動,為了執行例如建立和銷燬fragment的UI的動作. 這些額外的回撥方法是:
- onAttach()
當fragment被繫結到activity時被呼叫(Activity會被傳入.). - onCreateView()
建立和fragment關聯的view hierarchy時呼叫. - onActivityCreated()
當activity的onCreate()方法返回時被呼叫. - onDestroyView()
當和fragment關聯的view hierarchy正在被移除時呼叫. - onDetach()
當fragment從activity解除關聯時被呼叫.
fragment生命週期的流程, 以及宿主activity對它的影響,在圖3中顯示.在這個圖中,可以看到activity依次的每個狀態是如何決定fragment可能接收到的回撥方法.例如, 當activity接收到它的onCreate(),activity中的fragment接收到最多是onActivityCreated().
一旦activity到達了resumed狀態, 你可以自由地在activity新增和移除fragment.因此,僅當activity處於resumed狀態時, fragment的生命週期才可以獨立變化.
無論如何, 當activity離開resumed狀態,fragment再次被activity的推入它自己的生命週期過程.
(關於Example,後續)
相關文章
- Hadoop官網翻譯之HDFS ArchitectureHadoop
- Hadoop官網翻譯之HDFS Users GuideHadoopGUIIDE
- influxdb官網文件翻譯UX
- Android之FragmentAndroidFragment
- Gremlin-官網介紹翻譯REM
- hadoop官網翻譯之HDFS High Availability Using the Quorum Journal ManagerHadoopAI
- Gradle入門(翻譯自Graddle官網)Gradle
- spring官網線上學習文件翻譯Spring
- hadoop官網翻譯第一天Hadoop
- ARP協議:網路世界的地址翻譯官協議
- fragment之間相互傳資料、共享資料Fragment
- hadoop官網翻譯第三天Hadoop Cluster SetupHadoop
- Android面試題之Fragment篇Android面試題Fragment
- Elasticsearch官檔翻譯——2.6 升級Elasticsearch
- Android 布 局 翻 譯 器Android
- Fragment翻蓋動效Fragment
- Android筆記之Fragment的startActivityForResult(與requestPermissions)Android筆記Fragment
- Kotlin(android)協程中文翻譯KotlinAndroid
- 資料庫第九周翻譯資料庫
- 【工利其器】工具使用之(四)systrace篇(1)官網翻譯
- Android基礎—FragmentAndroidFragment
- Perl資料型別安全研究【翻譯】資料型別
- hadoop官網翻譯第二天Setting up a Single Node Cluster.Hadoop
- 【翻譯】ANDROID KTX – 使用Kotlin進行Android開發AndroidKotlin
- Python 使用白嫖網易翻譯 API 進行翻譯PythonAPI
- [翻譯] .NET 官宣跨平臺 UI 框架 MAUIUI框架
- Android--單Activity+多Fragment,玩轉FragmentAndroidFragment
- iBooker:中文翻譯資料電子書大全
- [非專業翻譯] Mapster - 資料型別資料型別
- [翻譯]資料結構——trie樹介紹資料結構
- Android 碎片(Fragment)講解AndroidFragment
- 學php之翻譯wordpress(2)PHP
- Android基礎進階之EffectiveJava翻譯系列(第八章:異AndroidJava
- WPF 被 靈格斯翻譯官 取詞帶崩
- Serilog文件翻譯系列(四) - 結構化資料
- 【翻譯】WPF中的資料繫結表示式
- Android 點將臺:撒豆成兵[- Fragment -]AndroidFragment
- Android Jetpack - Fragment官方說明AndroidJetpackFragment
- 論機器翻譯之淺薄