1. 程式人生 > >Android Fragment(三)---生命週期與回退棧

Android Fragment(三)---生命週期與回退棧

Fragment生命週期

與Activity類似,Fragment也存在如下狀態。

  • 執行狀態:當前Fragment位於前臺,使用者可見,可以獲得焦點。
  • 暫停:其他Activity位於前臺,該Fragment依然可見,只是不能獲得焦點。
  • 停止狀態:該Fragment不可見,失去焦點。
  • 銷燬狀態:該Fragment完全被刪除,或該Fragment所在的Activity被結束。

如下圖官方給出的Fragment生命週期圖:
The lifecycle of a fragment (while its activity is running).

  • onAttach():當該Fragment被新增到Activity時被呼叫。該方法只會被呼叫一次。
  • onCreate():建立Fragment時被呼叫。該方法只會被呼叫一次。
  • onCreateView():每次建立、繪製該Fragment的View元件時回撥該方法,Fragment將會顯示該方法返回的View元件。
  • onActivityCreated():當Fragment所在的Activity被啟動完成後回撥該方法。
  • onStart():啟動Fragment時被回撥。
  • onResume():恢復Fragment時被回撥。
  • onPause():暫停Fragment時被回撥。
  • onStop():停止Fragment時被回撥。
  • onDestroyView():銷燬該Fragment所包含的View元件時被呼叫。
  • onDestroy():銷燬Fragment時被回撥。該方法之後被呼叫一次。
  • onDetach():將該Fragment從Activity中刪除、替換完成時回撥該方法,在onDestroy()方法後一定會回撥onDetach()方法。該方法只會被呼叫一次。

從上圖可以看出:
1. 當使用Activity載入Fragment時,執行的函式為:
onAttach()
onCreate()
onCreateView()
onActivityCreated()
onStart()
onResume()

2.在Activity上啟動一個對話方塊,Activity將轉入暫停狀態時,Fragment也會進入暫停狀態。執行的函式為:onPause()

3.關閉對話方塊,Activity進入執行狀態,Fragment將會再次進入執行狀態,執行的函式為:onResume()
即由2到3,執行的函式為:onPause()—>onResume(),沒有重構介面佈局。因此,如果當按回退鍵,不想重構Fragment,要儲存原來Fragment中的資料,可以將Fragment隱藏(使用hide()函式),處於暫定狀態

4.如上圖左邊那條流程線,替換Fragment不加入回退棧或按退出按鈕,則Fragment例項會被銷燬。執行的函式為:
onPause()
onStop()
onDestroyView()
onDestroy()
onDetach()

5.如上圖右邊的那條流程線,替換Fragment並加入回退棧,Fragment的例項不會銷燬,只會銷燬Fragment的介面佈局。執行的函式為:
onPause()
onStop()
onDestroyView()
當按下手機的回退鍵時,Fragment將會再次顯示出來,執行的函式為:
onCreateView()
onActivityCreated()
onStart()
onResume()

回退棧

介紹回退棧之前先介紹下Fragment家族常用的API:
Fragment常用的三個類:

  • android.app.Fragment 主要用於定義Fragment

  • android.app.FragmentManager 主要用於在Activity中操作Fragment

  • android.app.FragmentTransaction 保證一些列Fragment操作的原子性,熟悉事務這個詞,一定能明白~

a、獲取FragmentManage的方式:

getFragmentManager() // v4中,getSupportFragmentManager

b、主要的操作都是FragmentTransaction的方法

  • FragmentTransaction transaction = fm.benginTransatcion();//開啟一個事務

  • transaction.add(): 往Activity中新增一個Fragment

  • transaction.remove() :從Activity中移除一個Fragment,如果被移除的Fragment沒有新增到回退棧,這個Fragment例項將會被銷燬。如果新增到回退棧,則這個Fragment的例項不會銷燬,但是它的佈局檢視會被銷燬。

  • transaction.replace():使用另一個Fragment替換當前的,實際上就是remove()然後add()的合體~

  • transaction.hide():隱藏當前的Fragment,僅僅是設為不可見,例項並不會銷燬,Fragment的佈局檢視也不會銷燬。

  • transaction.show():顯示之前隱藏的Fragment

  • detach():會將view從UI中移除,和remove()不同,此時fragment的狀態依然由FragmentManager維護。

  • attach():重建view檢視,附加到UI上並顯示。

  • transatcion.commit()//提交一個事務

注意:常用Fragment的哥們,可能會經常遇到這樣Activity狀態不一致:State loss這樣的錯誤。主要是因為:commit方法一定要在Activity.onSaveInstance()之前呼叫。

上述,基本是操作Fragment的所有的方式了,在一個事務開啟到提交可以進行多個的新增、移除、替換等操作。

注:此處的FragmentTransaction(事務)是一個狀態變化的過程,如

// 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();

如上面程式碼,transaction只是記錄了從一個狀態到另一個狀態的變化過程,即比如從FragmentA替換到FragmentB的過程,當通過函式transaction.addToBackStack(null)將這個事務新增到回退棧,則會記錄這個事務的狀態變化過程,如從FragmentA —>FragmentB,當用戶點選手機回退鍵時,因為transaction的狀態變化過程被儲存,則可以將事務的狀態變化過程還原,即將FragmentB —> FragmentA.

新增到回退棧的函式:transaction.addToBackStack(null);
官方解釋為:

Before you call commit(), however, you might want to call addToBackStack(), in order to add the transaction to a back stack of fragment transactions. This back stack is managed by the activity and allows the user to return to the previous fragment state, by pressing the Back button.

For example, here’s how you can replace one fragment with another, and preserve the previous state in the back stack:

// 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();

In this example, newFragment replaces whatever fragment (if any) is currently in the layout container identified by the R.id.fragment_container ID. By calling addToBackStack(), the replace transaction is saved to the back stack so the user can reverse the transaction and bring back the previous fragment by pressing the Back button.

當Activity的回退棧中沒有事務時,在按返回鍵則會退出Activity。

類似與Android系統為Activity維護一個任務棧,我們也可以通過Activity維護一個回退棧來儲存每次Fragment事務發生的變化。如果你將Fragment任務新增到回退棧,當用戶點選後退按鈕時,將看到上一次的儲存的Fragment。一旦Fragment完全從後退棧中彈出,使用者再次點選後退鍵,則退出當前Activity。

Demo:看如下效果
這裡寫圖片描述

在文字框中輸入”one”,點選按鈕,切換到第二個介面; 在文字框中輸入”two”,點選按鈕,切換到第三個介面;點選按鈕出現Toast提示訊息;然後點選Back鍵依次回退到FragmentTwo,文字框中的資料”two”還存在,再次點選Back鍵會退到FragmentOne,文字框中的資料”one”不存在了。

程式碼:一共3個Fragment和一個Activity

先看Activity:activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <FrameLayout
        android:id="@+id/frameLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"></FrameLayout>
</RelativeLayout>

不同的Fragment就在這個FrameLayout中顯示。

MainActivity.java

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        FragmentManager fragmentManager = getFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

        FragmentOne fragmentOne = new FragmentOne();
        fragmentTransaction.add(R.id.frameLayout,fragmentOne,"One");
        fragmentTransaction.commit();
    }
}

很簡單,直接將FragmentOne新增到佈局檔案中的FrameLayout中,注意這裡並沒有呼叫FragmentTransaction.addToBackStack(String),因為我不喜歡在當前顯示時,點選Back鍵出現白板。而正確的時點選相應Back鍵,即退出我們的Activity.

下面時3個Fragment的佈局檔案,大體相同,就只寫一個,fragment_one.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.songxitang.fragmenttest.FragmentOne">

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/fragOneBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="FragmentOne"
        android:textAllCaps="false" />
</LinearLayout>

FragmentOne.java

public class FragmentOne extends Fragment {
    private Button fragOneBtn;

    public FragmentOne() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_one, container, false);
        fragOneBtn = (Button) view.findViewById(R.id.fragOneBtn);
        fragOneBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                FragmentTwo fragmentTwo = new FragmentTwo();
                FragmentManager fragmentManager = getFragmentManager();
                FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
                fragmentTransaction.replace(R.id.frameLayout,fragmentTwo,"Two");
                //將事務新增到回退棧中
                fragmentTransaction.addToBackStack("FragmentTwo");
                fragmentTransaction.commit();
            }
        });
        return view;
    }
}

我們在點選FragmentOne中的按鈕時,使用了replace方法,replace是remove和add的合體,並且如果不新增事務到回退棧,前一個Fragment例項會被銷燬。這裡很明顯,我們呼叫tx.addToBackStack(null);將當前的事務新增到了回退棧,所以FragmentOne例項不會被銷燬,但是檢視層次依然會被銷燬,即會呼叫onDestoryView和onCreateView,證據就是:仔細看上面的效果圖,我們在跳轉前在文字框輸入的內容,在使用者Back得到第一個介面的時候不見了。

FragmentTwo.java

public class FragmentTwo extends Fragment {
    private Button fragTwoBtn;

    public FragmentTwo() {
        // Required empty public constructor
    }
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_two, container, false);
        fragTwoBtn = (Button) view.findViewById(R.id.fragTwoBtn);
        fragTwoBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                FragmentThree fragmentThree = new FragmentThree();
                FragmentManager fragmentManager = getFragmentManager();
                FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
                fragmentTransaction.hide(FragmentTwo.this);
                fragmentTransaction.add(R.id.frameLayout,fragmentThree,"Three");
                fragmentTransaction.addToBackStack(null);
                fragmentTransaction.commit();
            }
        });
        return view;
    }
}

這裡點選時,我們沒有使用replace,而是先隱藏了當前的Fragment,然後添加了FragmentThree的例項,最後將事務新增到回退棧。這樣做的目的是為了給大家提供一種方案:如果不希望檢視重繪該怎麼做,請再次仔細看效果圖,我們在FragmentTwo的EditText填寫的內容,使用者Back回來時,資料還在~~~

最後FragmentThree就是簡單的Toast了,FragmentThree.java:

public class FragmentThree extends Fragment {
    private Button fragThreeBtn;

    public FragmentThree() {
        // Required empty public constructor
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_three, container, false);
        fragThreeBtn = (Button) view.findViewById(R.id.fragThreeBtn);
        fragThreeBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(getActivity(),"I am a Button in FragmentThree!"
                ,Toast.LENGTH_LONG).show();
            }
        });
        return view;
    }
}

值得注意的是:如果你喜歡使用Fragment,一定要清楚這些方法,哪個會銷燬檢視,哪個會銷燬例項,哪個僅僅只是隱藏,這樣才能更好的使用它們。

a、比如:我在FragmentA中的EditText填了一些資料,當切換到FragmentB時,如果希望會到A還能看到資料,則適合你的就是hide和show;也就是說,希望保留使用者操作的面板,你可以使用hide和show,當然了不要使勁在那new例項,進行下非null判斷。

b、再比如:我不希望保留使用者操作,你可以使用remove(),然後add();或者使用replace()這個和remove,add是相同的效果。

c、remove和detach有一點細微的區別,在不考慮回退棧的情況下,remove會銷燬整個Fragment例項,而detach則只是銷燬其檢視結構,例項並不會被銷燬。那麼二者怎麼取捨使用呢?如果你的當前Activity一直存在,那麼在不希望保留使用者操作的時候,你可以優先使用detach。

程式碼下載

相關推薦

Android Fragment()---生命週期退

Fragment生命週期 與Activity類似,Fragment也存在如下狀態。 執行狀態:當前Fragment位於前臺,使用者可見,可以獲得焦點。 暫停:其他Activity位於前臺,該Fragment依然可見,只是不能獲得焦點。 停止狀態:該

Fragment進階篇之Fragment生命週期退

前言 上一篇blog(處女男學Android(八)---Fragment初體驗之實現Tab導航)記錄了fragment的基本概念和基本的使用方法,本篇將逐步深入記錄關於fragment的幾個重要知識點,包括:fragment的生命週期、fragm

Android Fragment生命週期+懶載入)

以前面試的時候吧,能把Fragment的生命週期從頭到尾背一遍,然後懶載入也是知道怎麼實現,但是呢,沒有寫過demo具體研究過,於是就準備寫篇部落格就當筆記了。 先附上一張面試時候常考的一張Fragment生命週期圖: 測試的佈局activity_mai

Fragment生命週期切換

準備重新入手安卓了,依然選擇從fragment開始。衝鴨~! Fragment有如下兩個選擇 android.support.v4.app.Fragment android.app.Fragment; support.v4.app下的片段具有更好的相容性,可以相容到1.

淺談Android中的 Fragment生命週期撥方法 以及使用

        4onActivityCreated()              當Activity中的onCreate方法執行完後呼叫。 注意了:從這句官方的話可以看出:當執行onActivityCreated()的時候 activity的onCreate才剛完成。所以在onActivityCrea

Android viewPager Fragment 切換生命週期

記錄一下: 場景: 上面是一個很常見的一個切換效果:結果一般都是一個主Activity,裡面佈局了一個TabLayout+ViewPager,ViewPager裡面添加了4個Fragment,假如日期時間為A,聲音設定為B 開關機設定為C,認證模式 為D 第一步:列印一下主要的Log

Fragment生命週期Fragment執行hide、show後的生命週期探討

一、Fragment 生命週期中的每個方法的意義與作用:     1.setUserVisibleHint()(此方法不屬於生命週期方法):設定Fragment 使用者可見或不可見時呼叫此方法,此方法在Fragment所有生命週期執行之前執行。當Fragment 可見狀態改變

Android開發— Activity生命週期fragment生命週期

(圖片就借鑑一下其他作者的,見諒!) Activity的生命週期在業務邏輯上的處理一定要慎重!!! Fragment的生命週期同樣十分重要,並且要常用frgment懶載入方案更要注意這種方式,其生命週期圖 同時我們都知道 Fragment 是依賴於 Acti

Maven學習():生命週期maven外掛

一、maven 生命週期 (一)簡介 Maven強大的一個重要的原因是它有一個十分完善的生命週期模型(lifecycle),這個生命週期可以從兩方面來理解:      1、顧名思義,執行Maven的每個步驟都由它來定義的,這種預定義的預設行為使得我們使用Ma

ActivityFragment生命週期詳解

在安卓中Activity與Fragment是非常相似的兩個類,它們各自都擁有自己的生命週期,且都可以用來顯示佈局檔案中的檢視。其中Activity是通過setContenView()顯示檢視,而Fra

Android測試Activity和Fragment生命週期

Activity的生命週期有7個函式,Fragment的生命週期有11個函式。 Activity生命週期除上述6個方法還有一個Restart()方法,該方法在該Activity從不可見(仍存在)到重新可見時呼叫。 測試程式碼如下: import android.a

ActivityFragment生命週期

一、Activity 生命週期 二、Fragment 生命週期 三、對比圖 四、測試程式碼 package com.goso.testapp; import android.app.Activity; import android.app.ListFragmen

Activity生命週期撥,你應該知道得更多!--Android原始碼剖析(上)

private class ApplicationThread extends ApplicationThreadNative { //... public final void schedulePauseActivity(IBinder token, boolean finished,

ActivityFragment生命週期對比

Fragment是3.0以後的東西,為了在低版本中使用Fragment就要用到android-support-v4.jar相容包,而FragmentActivity就是這個相容包裡面的,它提供了操作Fragment的一些方法,其功能跟3.0及以後的版本的Acti

Fragment生命週期以及Activity生命週期聯動

在學Fragment之前肯定學過了Activity,Activity有屬於自己的生命週期,Fragment基本上和activity 大體一樣,但是有自己特有的生命週期方法,下面我們一起來看一下。 說

fragment中onCreateViewonActivityCreated的區別,以及fragment生命週期的利用

最近使用了一個自定義的view在activity中執行正常,可在fragment中就奔潰,無提示,之前view是在onCreateView中初始化並呼叫的,崩潰,換到onActivityCreated之後,執行ok了,這是什麼原因呢?? 先看看fragment的生命週期,首

Android那些事》——Fragment生命週期及常見問題

一、Fragment的生命週期 二、與Activity生命週期的對比 在使用Fragment中常見的問題是空指標異常,一般出現這種情況是因為Activity的onCreate方法還沒執行完,而在Fragment的onCreateView方法中進行了例項化物件的操作,進而造成了

AndroidFragment退詳解

前言:本文將結合開發中的實際需求,來講解一下Fragment中的回退棧 對於Activity,當按返回鍵時,能夠返回到上一個Activity,但是,當我們Fragment到Activity中時,如果不做任何處理,當按返回鍵時,當前Fragment都會全部退出,如

Activity生命週期撥,你應該知道得更多!--Android原始碼剖析(下)

private void handleBindApplication(AppBindData data) { mBoundApplication = data; mConfiguration = new Configuration(data.co

Android中服務的生命週期兩種方式的區別

服務的生命週期跟Activity的生命週期類似。但是生命週期甚至比你關注服務如何建立和銷燬更重要,因為服務能夠在使用者不知情的情況下在後臺執行。 服務的生命週期---從建立到銷燬---可以被分為以下兩個路徑: 1.  啟動型別的服務: onCreate()- >onSt