相關文章:
1、《Fragment詳解之一——概述》
2、《Fragment詳解之二——基本使用方法》
3、《Fragment詳解之三——管理Fragment(1)》
4、《Fragment詳解之四——管理Fragment(2)》
5、《Fragment詳解之五——Fragment間參數傳遞》
6、《Fragment詳解之六——如何監聽fragment中的回退事件與怎樣保存fragment狀態》
前面給大家稍微看了要怎么使用fragment,在上篇中,我們也初步接觸到了add,replace這些fragment操作的函數,下面就再詳細講講如何管理Fragment頁面吧。
一、概述
1、FragmentManager
要管理activity中的fragments,你就需要使用FragmentManager。通過getFragmentManager()或getSupportFragmentManager()獲得
常用的方法有:
- manager.findFragmentById(); //根據ID來找到對應的Fragment實例,主要用在靜態添加fragment的布局中,因為靜態添加的fragment才會有ID
- manager.findFragmentByTag();//根據TAG找到對應的Fragment實例,主要用於在動態添加的fragment中,根據TAG來找到fragment實例
- manager.getFragments();//獲取所有被ADD進Activity中的Fragment
2、FragmentTransaction
一般用來對當前的Fragment進行管理,包括add,replace,remove;
常用的針對Fragment的方法有:
- //將一個fragment實例添加到Activity的最上層
- add(int containerViewId, Fragment fragment, String tag);
- //將一個fragment實例從Activity的fragment隊列中刪除
- remove(Fragment fragment);
- //替換containerViewId中的fragment實例,注意,它首先把containerViewId中所有fragment刪除,然后再add進去當前的fragment
- replace(int containerViewId, Fragment fragment);
還有hide()、show()、detach()、attach()這些函數,我們下篇再講,這節先對Fragment的用法有一個初步了解;
二、add()、replace()、remove()使用方法示例
下面就通過例子來看看以上幾個函數的使用方法吧。
效果圖如下:
- 點擊“ADD Fragment1”,在將Fragment1添加到Activity的container中;
- 點擊“ADD Fragment2”,將Fragment2添加到Activity的container中;
- 點擊“Remove Fragment2”,將Fragment2的實例從container中移除,移除之后,就顯示出其下方的fragment1的視圖出來了。
- 再點擊”replace Fragment1”,將container中的視圖移除,然后添加上fragment2的視圖。

那現在我們從頭開始構建這個工程:
1、新建兩個fragment1.xml 和 fragment2.xml:
從效果圖中也可以看出,這兩個XML什么都沒有,只是通過背景色和文字來區別當前是哪個Fragment的XML布局文件而已,他們的布局代碼如下:
fragment1.xml:
- <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>
2、建立對應的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);
- }
- }
3、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>
4、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進去的先后順序形成了一個鏈表,我們上面的操作在這個列表中的形式變化如下圖所示:
源碼在文章最底部給出
到這里add,replace,remove的使用方法就講完了,但這里有個問題,必須根大家講一下:我們上面說過replace操作會把container中的所有fragment全部刪除,然后再將指定的fragment添加進去!但Android在實現時出現了BUG!在replace()時,並不能把以前所有Fragment清空,就因為這個系統工程產了BUG就會導致add()和Replace()不能共用!關於add()和Replace()不能共用的問題,我們會在下篇再講。下面先給大家說說有關回滾的問題。
三、有關回滾——FragmentTransaction
1、FragmentTransaction事務回滾使用方法:
上部分,我們講了有關添加、刪除Fragment的操作,想將上一次commit的操作返回時,要怎么做呢。這就需要FragmentTransaction的回滾功能了。
要使用回滾功能,只需要要使用下面兩個代碼:
在transaction.commit()之前,使用addToBackStack()將其添加到回退棧中。
- transaction.addToBackStack(String tag);
在需要回退時,使用popBackStack()將最上層的操作彈出回退棧。
- manager.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)
下面我們就通過一個例子來講解一下有關回退棧中的操作過程:
先看下效果圖:

整體流程是這樣的:
1、逐個將Fragment1,2,3,4添加到窗口中,在添加時,每添加一個Fragment要利用transaction的addToBackStack將此次操作加入到回退棧中。
2、然后點擊"PopBackStack"方法,將棧頂最上層的操作回退。退將最后一次添加回退出來,顯示Fragment3.
3、點擊“ADD Fragment4”將棧還原到1,2,3,4依次ADD進棧的狀態,即操作1完成后的棧狀態,然后點擊“BackToStack2_0”,其實調用的方法是:
- manager.popBackStack("fragment2",0);//方法一,通過TAG回退
從這里可以看出,要回退到添加ADD Fragment2的狀態,注意最后一個參數,這里設為0,表明,要回退ADD Fragment2的之后的操作,將ADD Fragment2的操作置為棧頂。從效果圖中也可以看出,點擊后的視圖在Fragment2的位置
4、最后仍然是先點擊"Add Fragment3"和"ADD Fragment4",將棧還原到操作1完成后的棧狀態。然后點擊“BackToStack2_INCLUSIVE”;其調用的方法是:
- manager.popBackStack("fragment2",FragmentManager.POP_BACK_STACK_INCLUSIVE);//方法一,通過TAG回退
這里與上面的主要不同點在於第二個參數,這里設置為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);//方法一,通過TAG回退
- // manager.popBackStack(stackID2,0);//方法二,通過Transaction ID回退
- }
(5)、最后是點擊BackToFrag2_INCLUSIVE按鈕的代碼:
- private void popBackStackToFrag2_Inclusive(){
- FragmentManager manager = getSupportFragmentManager();
- manager.popBackStack("fragment2",FragmentManager.POP_BACK_STACK_INCLUSIVE);//方法一,通過TAG回退
- // manager.popBackStack(stackID2,FragmentManager.POP_BACK_STACK_INCLUSIVE);//方法二,通過Transaction ID回退
- }
好了,到這里,有關回滾的基本使用就結束了,需要要注意的是:
使用popBackStack()來彈出棧內容的話,調用該方法后會將事物操作插入到FragmentManager的操作隊列,只有當輪詢到該事物時才能執行。如果想立即執行事物的話,需要使用下面幾個對應的方法:
- popBackStackImmediate()
- popBackStackImmediate(String tag)
- popBackStackImmediate(String tag, int flag)
- popBackStackImmediate(int id, int flag)
2、回退棧(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() {
- // TODO Auto-generated method stub
- Log.d("qijian","backstack changed");
- }
- };
- manager.addOnBackStackChangedListener(listener);
(2)、當onDestory()中將監聽器remove掉:
- protected void onDestroy() {
- // TODO Auto-generated method stub
- super.onDestroy();
- FragmentManager manager = getSupportFragmentManager();
- manager.removeOnBackStackChangedListener(listener);
- }
大家一定要注意,不管是這里的回退棧的監聽還是其它的監聽器,在頁面對應的銷毀時,都要記得remove掉,不然會造成頁面不釋放,這也是造成OOM的問題之一。
這樣當回退棧內容出現變動時,變會打LOG出來,如圖:

3、Transaction事務回退的原則
這里我們着重講一下,回退是以commit()提交的一次事務為單位的,而不是以其中的add,replace等等操作為單位回退的,即,如果我們在一次提交是添加了fragment2,fragment3,fragment4,那么回退時,會依據添加時的順序,將它們一個個刪除,返回到沒有添加fragment4,fragment3,fragment2的狀態。
下面我們仍然寫一個例子來講明一下事務的回退原則,效果圖如下:

- 1、首先,添加Fragment1,提交一次事務
- 2、然后,一次添加Fragment2,Fragment3,Fragment4,然后提交一次事務
- 3、利用popBackStack()將頂層事務出棧,可以看到把Fragment2,Fragment3,Fragment4一次出棧,界面顯示在了Fragment1的位置,這就充分說明了,回滾是以提交的事務為單位進行的!
下面是代碼實現部分:
1、同樣,新建四個Fragment,分別利用背景色和文字來表明之間的不同。
2、然后添加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();
- }
3、然后是添加其它三個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();
- }
再一次重申,從代碼中也可以看到,是依次將Fragment2、Fragment3、Fragment4加入容器之后,再提交一次事務,所以下面回滾時,會將他們反順序的依次刪除。即remove(fragment4)、remove(fragment3)、remove(fragment2)
4、點擊popBackStack按鈕時的代碼
- private void popBackStack() {
- FragmentManager manager = getSupportFragmentManager();
- manager.popBackStack();
- }
好了,到這里這篇文章就結束了,這篇文章太TM長了……大家估計看得也快要吐了……知識點太多,又想能給大家講的淺顯易懂就只有拉長篇幅了……
源碼中有三個工程:
1、《harvicBlog3_1》:對應第二部分:add()、replace()、remove()使用方法示例
2、《harvicBlog3_2》:對應第三部分《有關回滾——FragmentTransaction》中的:FragmentTransaction事務回滾使用方法和回退棧內容監聽部分
3、《harvicBlog3_3》:對應第三部分《有關回滾——FragmentTransaction》中的:Transaction事務回退的原則部分
如果本文有幫到你,記得加關注哦
源碼下載地址:http://download.csdn.net/detail/harvic880925/8578519
請大家尊重原創者版權,轉載請標明出處:http://blog.csdn.net/harvic880925/article/details/44927375, 謝謝!
