相關文章:html
一、《Fragment詳解之一——概述》
二、《Fragment詳解之二——基本使用方法》
三、《Fragment詳解之三——管理Fragment(1)》
四、《Fragment詳解之四——管理Fragment(2)》
五、《Fragment詳解之五——Fragment間參數傳遞》
六、《Fragment詳解之六——如何監聽fragment中的回退事件與怎樣保存fragment狀態》java
前面給你們稍微看了要怎麼使用fragment,在上篇中,咱們也初步接觸到了add,replace這些fragment操做的函數,下面就再詳細講講如何管理Fragment頁面吧。android
1、概述
一、FragmentManager
要管理activity中的fragments,你就須要使用FragmentManager。經過getFragmentManager()或getSupportFragmentManager()得到
經常使用的方法有:數據庫
- manager.findFragmentById();
- manager.findFragmentByTag();
- manager.getFragments();
二、FragmentTransaction
通常用來對當前的Fragment進行管理,包括add,replace,remove;
經常使用的針對Fragment的方法有:app
- add(int containerViewId, Fragment fragment, String tag);
- remove(Fragment fragment);
- replace(int containerViewId, Fragment fragment);
還有hide()、show()、detach()、attach()這些函數,咱們下篇再講,這節先對Fragment的用法有一個初步瞭解;ide
2、add()、replace()、remove()使用方法示例
下面就經過例子來看看以上幾個函數的使用方法吧。
效果圖以下:svg
- 點擊「ADD Fragment1」,在將Fragment1添加到Activity的container中;
- 點擊「ADD Fragment2」,將Fragment2添加到Activity的container中;
- 點擊「Remove Fragment2」,將Fragment2的實例從container中移除,移除以後,就顯示出其下方的fragment1的視圖出來了。
- 再點擊」replace Fragment1」,將container中的視圖移除,而後添加上fragment2的視圖。
![](http://static.javashuo.com/static/loading.gif)
那如今咱們從頭開始構建這個工程:函數
一、新建兩個fragment1.xml 和 fragment2.xml:
從效果圖中也能夠看出,這兩個XML什麼都沒有,只是經過背景色和文字來區別當前是哪一個Fragment的XML佈局文件而已,他們的佈局代碼以下:佈局
fragment1.xml:spa
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="#ff00f0"
- android:orientation="vertical" >
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="This is fragment 1"
- android:textColor="#000000"
- android:textSize="25sp" />
-
- </LinearLayout>
fragment2.xml:
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="#ffff00"
- android:orientation="vertical" >
-
- <TextView
- android:id="@+id/fragment2_tv"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="This is fragment 2"
- android:textColor="#000000"
- android:textSize="25sp" />
-
- </LinearLayout>
二、創建對應的Fragment類:Fragment1和Fragment2
Fragment1:
- import android.os.Bundle;
- import android.support.v4.app.Fragment;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.ViewGroup;
-
- public class Fragment1 extends Fragment {
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- return inflater.inflate(R.layout.fragment1, container, false);
- }
-
- }
與上一篇同樣,也只是在onCreateView()時返回對應的佈局。一樣,Fragment2的定義以下:
- import android.os.Bundle;
- import android.support.v4.app.Fragment;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.ViewGroup;
-
- public class Fragment2 extends Fragment {
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- return inflater.inflate(R.layout.fragment2, container, false);
- }
- }
三、MainActivity的佈局
從上面的的效果圖中也能夠看出大概的佈局,首先是三個Button,最下方是一個FrameLayout佈局,它是用來作爲container動態盛裝fragment的;它就像是一個佔位符,你設置多大,它其中的fragment就最大能有多大。記住,fragment也是Activity中的一個普通控件而已,只不過它能夠像Activity同樣用於顯示的同時還能用來盛裝其它控件!做爲fragment的容器,便可以用FrameLayout也能夠用LinearLayout或者RelativeLayout,都是同樣的。activity_main.xml的佈局代碼以下:
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <Button
- android:id="@+id/btn_add_frag1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="ADD Fragment1" />
-
- <Button
- android:id="@+id/btn_add_frag2"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="ADD Fragment2" />
-
- <Button
- android:id="@+id/btn_remove_frag2"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="Remove Fragment2" />
-
- <Button
- android:id="@+id/btn_repalce_frag1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="replace Fragment1" />
-
- <FrameLayout
- android:id="@+id/fragment_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
-
- </LinearLayout>
四、MainActivity的實現:
(1)首先,先寫一個添加fragment到Activity中的函數:
- private void addFragment(Fragment fragment, String tag) {
- FragmentManager manager = getSupportFragmentManager();
- FragmentTransaction transaction = manager.beginTransaction();
- transaction.add(R.id.fragment_container, fragment, tag);
- transaction.commit();
- }
- 先是傳進來兩個參數,一個是要添加的fragment實例,另外一個是一個TAG
- 至於FragmentManager、FragmentTransaction就沒什麼好講的,這是這麼個流程,要獲取它們的實例就得調用這些函數。
- 而後是add(R.id.fragment_container, fragment, tag)函數:第一個參數是要將fragment盛裝的container,即咱們上面的FrameLayout!第三個參數是tag,當傳進去這個TAG,它就會跟這個fragment關聯起來,當咱們經過findFragmentByTag()時,根據這個TAG就能找到這個Fragment實例。進而對它進行操做,好比,咱們下面的remove();
- 在經過transaction對Fragment操做完之後,必定要記得調用transaction.commit(),這樣纔會將操做提交到系統中,這裏的代碼纔會最終起做用。
有沒有以爲這個流程挺像數據庫的回滾操做!對,其實就是這樣的,這裏其實就是一個針對Fragment的回滾操做,首先經過beginTransaction()來定義回滾的開始,而後經過transaction對Fragment進行一系列操做(咱們這裏只是進行了ADD,其實能夠作一串操做),等操做完了利用commit()提交。這裏就先初步講到這,下節會細講有關transaction的回滾過程。
(2)添加Fragment1和Fragment2:
好了,上面咱們寫好了一個函數addFragment(Fragment fragment, String tag),下面咱們就要經過這個函數來添加Fragment1和Fragment2的實例了。
當點擊「ADD Fragment1」按鈕時:
- Fragment1 fragment1 = new Fragment1();
- addFragment(fragment1, "fragment1");
當點擊「ADD Fragment2」按鈕時:
- Fragment2 fragment2 = new Fragment2();
- addFragment(fragment2, "fragment2");
這裏須要注意的是,當咱們添加每一個Fragment實例時,都傳進去一個對應的TAG,fragment1對應的是「fragment1」,fragment2對應的是「fragment2」,經過這些TAG,咱們就能夠經過findFragmentByTag()來獲取它們了。
(3)RemoveFragment2:
下面就是當點擊「RemoveFragment2」按鈕時的代碼操做了:
- private void removeFragment2() {
- FragmentManager manager = getSupportFragmentManager();
- Fragment fragment = manager.findFragmentByTag("fragment2");
- FragmentTransaction transaction = manager.beginTransaction();
- transaction.remove(fragment);
- transaction.commit();
- }
首先是經過
- Fragment fragment = manager.findFragmentByTag("fragment2");
找到咱們當時ADD進Activity的fragment2實例,而後經過transaction.remove(fragment);將它刪除;從效果圖中能夠看到,因爲咱們移除了fragment2的實例,而又因爲fragment2是在界面最上方的,因此把它刪除了以後,天然就剩下了fragment1在最上方了,因此咱們就看到了fragment1的界面。那若是咱們移除的是fragment1呢?答案是界面沒有任何變化!由於fragment1的實例是在fragment2的下方的,咱們是根本看不到它的。
(4)ReplaceFragment1:
最後一個操做:ReplaceFragment1:
我們先回到上面的RemoveFragment2操做,在RemoveFragment2以後,要知道咱們的Activity的ADD隊列中,就只有fragment1了。知道這一點以後,我們看下面ReplaceFragment1的代碼:
- private void replaceFragment1() {
- FragmentManager manager = getSupportFragmentManager();
- Fragment2 fragment2 = new Fragment2();
- FragmentTransaction transaction = manager.beginTransaction();
- transaction.replace(R.id.fragment_container, fragment2);
- transaction.commit();
- }
這裏有個注意的問題:
- transaction.replace(R.id.fragment_container, fragment2);
這裏replace傳進去的第一個參數是容器ID,第二個參數是要新增的fragment。既然要replace(),那它是針對哪一個fragment進行replace()呢?怎麼沒有指定要替換的fragment!爲何這裏的第一個參數是盛裝Activity中全部Fragment的container的ID呢?沒錯,這裏的replace操做會把這個cotainer中全部fragment清空!!!!而後再把fragment2添加進去!
從上面的的講解,你們可能也已經覺查到FragmentTransaction的Add()操做是維持着一個隊列的,在這個隊列中,根據ADD進去的前後順序造成了一個鏈表,咱們上面的操做在這個列表中的形式變化以下圖所示:
![](http://static.javashuo.com/static/loading.gif)
源碼在文章最底部給出
到這裏add,replace,remove的使用方法就講完了,但這裏有個問題,必須根你們講一下:咱們上面說過replace操做會把container中的全部fragment所有刪除,而後再將指定的fragment添加進去!但Android在實現時出現了BUG!在replace()時,並不能把之前全部Fragment清空,就由於這個系統工程產了BUG就會致使add()和Replace()不能共用!關於add()和Replace()不能共用的問題,咱們會在下篇再講。下面先給你們說說有關回滾的問題。
3、有關回滾——FragmentTransaction
一、FragmentTransaction事務回滾使用方法:
上部分,咱們講了有關添加、刪除Fragment的操做,想將上一次commit的操做返回時,要怎麼作呢。這就須要FragmentTransaction的回滾功能了。
要使用回滾功能,只須要要使用下面兩個代碼:
在transaction.commit()以前,使用addToBackStack()將其添加到回退棧中。
- transaction.addToBackStack(String tag);
在須要回退時,使用popBackStack()將最上層的操做彈出回退棧。
這裏的popBackStack()是彈出默認的最上層的棧頂內容。
當棧中有多層時,咱們能夠根據id或TAG標識來指定彈出到的操做所在層。函數以下:
- void popBackStack(int id, int flags);
- void popBackStack(String name, int flags);
其中
- 參數int id是當提交變動時transaction.commit()的返回值。
- 參數string name是transaction.addToBackStack(String tag)中的tag值;
- 至於int flags有兩個取值:0或FragmentManager.POP_BACK_STACK_INCLUSIVE;
- 當取值0時,表示除了參數一指定這一層之上的全部層都退出棧,指定的這一層爲棧頂層;
- 當取值POP_BACK_STACK_INCLUSIVE時,表示連着參數一指定的這一層一塊兒退出棧;
- 除了這幾個函數,還有下面幾個函數:有關他們的使用,咱們在這小部分結尾時會提到
- popBackStackImmediate()
- popBackStackImmediate(String tag)
- popBackStackImmediate(String tag, int flag)
- popBackStackImmediate(int id, int flag)
下面咱們就經過一個例子來說解一下有關回退棧中的操做過程:
先看下效果圖:
![](http://static.javashuo.com/static/loading.gif)
總體流程是這樣的:
一、逐個將Fragment1,2,3,4添加到窗口中,在添加時,每添加一個Fragment要利用transaction的addToBackStack將這次操做加入到回退棧中。
二、而後點擊"PopBackStack"方法,將棧頂最上層的操做回退。退將最後一次添加回退出來,顯示Fragment3.
三、點擊「ADD Fragment4」將棧還原到1,2,3,4依次ADD進棧的狀態,即操做1完成後的棧狀態,而後點擊「BackToStack2_0」,其實調用的方法是:
- manager.popBackStack("fragment2",0);
從這裏能夠看出,要回退到添加ADD Fragment2的狀態,注意最後一個參數,這裏設爲0,代表,要回退ADD Fragment2的以後的操做,將ADD Fragment2的操做置爲棧頂。從效果圖中也能夠看出,點擊後的視圖在Fragment2的位置
四、最後仍然是先點擊"Add Fragment3"和"ADD Fragment4",將棧還原到操做1完成後的棧狀態。而後點擊「BackToStack2_INCLUSIVE」;其調用的方法是:
- manager.popBackStack("fragment2",FragmentManager.POP_BACK_STACK_INCLUSIVE);
這裏與上面的主要不一樣點在於第二個參數,這裏設置爲POP_BACK_STACK_INCLUSIVE,即在出棧時連帶ADD Fragment2的操做一塊出棧,放在棧頂的是ADD Fragment1的操做,因此放在界面上就是顯示的是Fragment1的視圖。
下面咱們看看具體實現:
(1)、首先,與上部分同樣,先添加四個Fragment,並用背景色和文字來區分。這部分代碼咱們就不講了。
主要看看點擊按鈕的代碼處理方法。
(2)、首先是添加Fragment1:
- Fragment1 fragment1 = new Fragment1();
- stackID1 = addFragment(fragment1,"fragment1");
其中:
- private int stackID1,stackID2,stackID3,stackID4;
- private int addFragment(Fragment fragment,String stackName){
- FragmentManager manager = getSupportFragmentManager();
- FragmentTransaction transaction = manager.beginTransaction();
- transaction.add(R.id.fragment_container,fragment);
- transaction.addToBackStack(stackName);
- return transaction.commit();
- }
首先,這裏的stackID1,stackID2,stackID3,stackID4是用來保存每次commit()後返回的Transaction的ID值。在void popBackStack(int id, int flags);時,其中的參數id就是這個值
而後在每次ADD操做後,利用addToBackStack(string name)將每次ADD操做添加進回退棧中;
一樣,添加Fragment2的代碼以下,添加Fragment3,Fragment4的方法同理
- Fragment2 fragment2 = new Fragment2();
- stackID2 = addFragment(fragment2,"fragment2");
(3)、而後是回退棧頂內容:
- private void popBackStack(){
- FragmentManager manager = getSupportFragmentManager();
- manager.popBackStack();
- }
(4)、接着是點擊BackToFrag2_0按鈕的內容,這裏有兩種方法實現,一種是指定TAG,一種是利用Commit()返回的ID
- private void popBackStackToFrag2_0(){
- FragmentManager manager = getSupportFragmentManager();
- manager.popBackStack("fragment2",0);
-
- }
(5)、最後是點擊BackToFrag2_INCLUSIVE按鈕的代碼:
- private void popBackStackToFrag2_Inclusive(){
- FragmentManager manager = getSupportFragmentManager();
- manager.popBackStack("fragment2",FragmentManager.POP_BACK_STACK_INCLUSIVE);
- }
好了,到這裏,有關回滾的基本使用就結束了,須要要注意的是:
使用popBackStack()來彈出棧內容的話,調用該方法後會將事物操做插入到FragmentManager的操做隊列,只有當輪詢到該事物時才能執行。若是想當即執行事物的話,須要使用下面幾個對應的方法:
- popBackStackImmediate()
- popBackStackImmediate(String tag)
- popBackStackImmediate(String tag, int flag)
- popBackStackImmediate(int id, int flag)
二、回退棧(back stack)狀態改變監聽
FragmentManager還爲咱們提供了監控回退棧狀態改變的方法:
- FragmentManager::addOnBackStackChangedListener(listener);
- FragmentManager::removeOnBackStackChangedListener(listener);
經過添加監聽器,就能夠在回退棧內容改變時,及時收到通知;
咱們在上面代碼的基礎上,在MainAcitivy中爲FragmentManager添加一個監聽器,當回退棧狀態改變時,打出一個LOG。具體實現以下:
(1)、OnCreate()中:
爲fragmentManger添加一個監聽器:
- FragmentManager manager = getSupportFragmentManager();
- listener = new FragmentManager.OnBackStackChangedListener() {
-
- @Override
- public void onBackStackChanged() {
-
- Log.d("qijian","backstack changed");
- }
- };
- manager.addOnBackStackChangedListener(listener);
(2)、當onDestory()中將監聽器remove掉:
- protected void onDestroy() {
-
- super.onDestroy();
- FragmentManager manager = getSupportFragmentManager();
- manager.removeOnBackStackChangedListener(listener);
- }
你們必定要注意,不論是這裏的回退棧的監聽仍是其它的監聽器,在頁面對應的銷燬時,都要記得remove掉,否則會形成頁面不釋放,這也是形成OOM的問題之一。
這樣當回退棧內容出現變更時,變會打LOG出來,如圖:
![](http://static.javashuo.com/static/loading.gif)
源碼在文章底部給出
三、Transaction事務回退的原則
這裏咱們着重講一下,回退是以commit()提交的一次事務爲單位的,而不是以其中的add,replace等等操做爲單位回退的,即,若是咱們在一次提交是添加了fragment2,fragment3,fragment4,那麼回退時,會依據添加時的順序,將它們一個個刪除,返回到沒有添加fragment4,fragment3,fragment2的狀態。
下面咱們仍然寫一個例子來說明一下事務的回退原則,效果圖以下:
![](http://static.javashuo.com/static/loading.gif)
- 一、首先,添加Fragment1,提交一次事務
- 二、而後,一次添加Fragment2,Fragment3,Fragment4,而後提交一次事務
- 三、利用popBackStack()將頂層事務出棧,能夠看到把Fragment2,Fragment3,Fragment4一次出棧,界面顯示在了Fragment1的位置,這就充分說明了,回滾是以提交的事務爲單位進行的!
下面是代碼實現部分:
一、一樣,新建四個Fragment,分別利用背景色和文字來代表之間的不一樣。
二、而後添加Fragment1的代碼以下:
- private void addFragment1() {
- Fragment1 fragment1 = new Fragment1();
- FragmentManager manager = getSupportFragmentManager();
- FragmentTransaction transaction = manager.beginTransaction();
- transaction.add(R.id.fragment_container, fragment1);
- transaction.addToBackStack("fragment1");
- transaction.commit();
- }
三、而後是添加其它三個Fragment的代碼以下:
- private void addOtherFragments() {
- Fragment2 fragment2 = new Fragment2();
- Fragment3 fragment3 = new Fragment3();
- Fragment4 fragment4 = new Fragment4();
- FragmentManager manager = getSupportFragmentManager();
- FragmentTransaction transaction = manager.beginTransaction();
- transaction.add(R.id.fragment_container, fragment2);
- transaction.add(R.id.fragment_container, fragment3);
- transaction.add(R.id.fragment_container, fragment4);
- transaction.addToBackStack("other fragments");
- transaction.commit();
- }
再一次重申,從代碼中也能夠看到,是依次將Fragment二、Fragment三、Fragment4加入容器以後,再提交一次事務,因此下面回滾時,會將他們反順序的依次刪除。即remove(fragment4)、remove(fragment3)、remove(fragment2)
四、點擊popBackStack按鈕時的代碼
- private void popBackStack() {
- FragmentManager manager = getSupportFragmentManager();
- manager.popBackStack();
- }
好了,到這裏這篇文章就結束了,這篇文章太TM長了……你們估計看得也快要吐了……知識點太多,又想能給你們講的淺顯易懂就只有拉長篇幅了……
源碼中有三個工程:
一、《harvicBlog3_1》:對應第二部分:add()、replace()、remove()使用方法示例
二、《harvicBlog3_2》:對應第三部分《有關回滾——FragmentTransaction》中的:FragmentTransaction事務回滾使用方法和回退棧內容監聽部分
三、《harvicBlog3_3》:對應第三部分《有關回滾——FragmentTransaction》中的:Transaction事務回退的原則部分
若是本文有幫到你,記得加關注哦
源碼下載地址:http://download.csdn.net/detail/harvic880925/8578519
請你們尊重原創者版權,轉載請標明出處:http://blog.csdn.net/harvic880925/article/details/44927375, 謝謝!