1. 程式人生 > >Android學習之碎片的生命週期

Android學習之碎片的生命週期

一、碎片的狀態:
1、執行狀態:當一個碎片是可見的,並且它所關聯的活動正處於執行狀態時,該碎片也處於執行狀態。
2、暫停狀態:當一個活動進入暫停狀態時(由於另一個未佔滿螢幕的活動被新增到了棧頂),與它相關聯的可見碎片就會進入到暫停狀態。
3、停止狀態:當一個活動進入停止狀態時,與它相關聯的碎片就會進入到停止狀態。或者通過呼叫 FragmentTransaction 的 remove()、replace()方法將碎片從活動中移除,但有在事務提交之前呼叫addToBackStack()方法,這時的碎片也會進入到停止狀態。總的來說,進入停止狀態的碎片對使用者來說是完全不可見的,有可能會被系統回收。
4、銷燬狀態:碎片總是依附於活動而存在的,因此當活動被銷燬時,與它相關聯的碎片就會進入到銷燬狀態。或者通過呼叫FragmentTransaction 的 remove()、replace()方法將碎片從活動中移除,但在事務提交之前並沒有呼叫 addToBackStack()方法,這時的碎片也會進入到銷燬狀態。

二、碎片的回撥方法:
Fragment類提供了一系列的回撥方法,以覆蓋碎片的每個環節,主要的回撥方法有:
1、onAttach():當碎片和活動建立關聯的時候呼叫。
2、onCreateView():為碎片建立檢視(載入佈局)時呼叫。
3、onActivityCreated():確保與碎片相關聯的活動一定已經建立完畢的時候呼叫。
4、onDestroyView():當與碎片關聯的檢視被移除的時候呼叫。
5、onDetach():當碎片和活動解除關聯的時候呼叫。

三、碎片完整的生命週期,如下圖所示,圖片來自Android官網:
這裡寫圖片描述

四、接下來附上一個例子來實踐一下碎片的生命週期:
1、首先新建一個專案,專案名為FragmentTest4,專案結構圖如下:
這裡寫圖片描述

2、專案效果如下:
這裡寫圖片描述
點選Hello Lc按鈕後,如下圖所示:
這裡寫圖片描述

3、首先,新建3個碎片的佈局檔案,分別為fragment1.xml,fragment2.xml,fragment3.xml,其中第三個碎片的佈局中的TextView設定無內容,程式碼分別如下:
fragment1.xml:

<?xml version="1.0" encoding="utf-8"?>
<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/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="Hello Lc"/> </LinearLayout>

fragment2.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView 
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="Hello xg"
        android:textSize="20sp"/>

</LinearLayout>

fragment3.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView 
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:textSize="20sp"/>

</LinearLayout>

4、新建三個類,分別為Fragment1,Fragment2,Fragment3類,繼承於Fragment類,用來載入碎片佈局檔案,程式碼分別如下:
Fragment1.java:

package com.example.fragmenttest4;

import android.app.Fragment;
import android.os.Bundle;
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) {
        // TODO Auto-generated method stub
        View view=inflater.inflate(R.layout.fragment1, container);
        return view;
    }

}

Fragment2.java,分別添加了碎片的回撥方法,列印相關的資訊:

package com.example.fragmenttest4;

import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class Fragment2 extends Fragment {

    public static final String TAG = "Fragment2";//宣告一個字串常量,列印資訊時的TAG
    public TextView textView;//宣告一個TextView物件
    private Fragment2 fragment2;//宣告Fragment2物件
    private String xg;//宣告字串物件

    @Override
    public void onAttach(Activity activity) {
        // TODO Auto-generated method stub
        Log.i(TAG, "onAttach");
        super.onAttach(activity);
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        Log.i(TAG, "onCreate");
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        Log.i(TAG, "onCreateView");
        View view = inflater.inflate(R.layout.fragment2, container);//載入fragment2.xml佈局檔案
        textView=(TextView)view.findViewById(R.id.textView);//獲得fragment2.xml檔案的TextView控制元件例項
        MainActivity activity=(MainActivity) getActivity();//獲得MainActivity例項
        fragment2=(Fragment2) activity.getFragmentManager().findFragmentById(R.id.fragment2);//通過活動例項獲得Fragment2物件
        xg=fragment2.textView.getText().toString();//獲取文字內容
        return view;//返回此檢視
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        Log.i(TAG, "onActivityCreated");
        super.onActivityCreated(savedInstanceState);
    }

    @Override
    public void onStart() {
        // TODO Auto-generated method stub
        Log.i(TAG, "onStart");
        super.onStart();
    }

    @Override
    public void onResume() {
        // TODO Auto-generated method stub
        Log.i(TAG, "onResume");
        //假如此碎片恢復的話
        if(fragment2.isResumed()){
            textView.setText(xg);//設定文字檢視顯示
        }
        super.onResume();
    }

    @Override
    public void onPause() {
        // TODO Auto-generated method stub
        Log.i(TAG, "onPause");
        super.onPause();
    }

    @Override
    public void onStop() {
        // TODO Auto-generated method stub
        Log.i(TAG, "onStop");
        super.onStop();
    }

    @Override
    public void onDestroyView() {
        // TODO Auto-generated method stub
        Log.i(TAG, "onDestoryView");
        super.onDestroyView();
    }

    @Override
    public void onDestroy() {
        // TODO Auto-generated method stub
        Log.i(TAG, "onDestroy");
        super.onDestroy();
    }

    @Override
    public void onDetach() {
        // TODO Auto-generated method stub
        Log.i(TAG, "onDetach");
        super.onDetach();
    }

}

Fragment3.java:

package com.example.fragmenttest4;

import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class Fragment3 extends Fragment {

    public TextView textView1;
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View view=inflater.inflate(R.layout.fragment3, container,false);//載入fragment3.xml佈局檔案
        textView1=(TextView)view.findViewById(R.id.textView1);//獲取fragment3.xml檔案的TextView控制元件例項
        MainActivity activity=(MainActivity) getActivity();//獲取MainActivity活動例項
        Fragment2 fragment2=(Fragment2) activity.getFragmentManager().findFragmentById(R.id.fragment2);//通過活動例項獲取Fragment2物件
        fragment2.textView.setText("");//設定Fragment2碎片的TextView為空
        String text=activity.button.getText().toString();//獲取到MainActivity中按鈕的文字內容
        textView1.setText(text);//設定Fragment3碎片的TextView的內容
        return view;//返回檢視
    }

}

我在上面加了一些邏輯,此專案點選Hello Lc按鈕後,右邊碎片改變為此按鈕的文字內容“Hello Lc”,而點選返回鍵要返回上一個碎片,所以必須在Fragment2類中的onResume()方法重新設定該文字內容。

5、接著開啟預設的activity_main.xml佈局檔案,用來新增兩個碎片。其中右邊碎片放在FrameLayout容器中,以便動態呼叫碎片,程式碼如下:

<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" >

    <fragment 
        android:id="@+id/fragment1"
        android:name="com.example.fragmenttest4.Fragment1"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"/>

    <FrameLayout
        android:id="@+id/frameLayout"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" >

        <fragment 
            android:id="@+id/fragment2"
            android:name="com.example.fragmenttest4.Fragment2"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

    </FrameLayout>
</LinearLayout>

6、修改預設類MianActivity,實現了動態呼叫碎片的方法,程式碼如下:

package com.example.fragmenttest4;

import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity {

    public Button button;//宣告Button物件

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);//載入activity_main.xml檔案
        button = (Button) findViewById(R.id.button);//獲取到左邊碎片的按鈕控制元件例項
        //新增按鈕點選事件監聽器
        button.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View arg0) {
                // TODO Auto-generated method stub
                Fragment3 fragment3=new Fragment3();//例項化Fragment3物件
                FragmentManager fragmentManager=getFragmentManager();//獲取FragmentManager物件
                FragmentTransaction fragmentTransaction=fragmentManager.beginTransaction();//開始事務
                fragmentTransaction.replace(R.id.frameLayout, fragment3);//動態呼叫碎片,即將原始右邊碎片Fragment2改為Fragment3碎片
                fragmentTransaction.addToBackStack(null);//新增到返回棧,即點選返回鍵返回上一個碎片
                fragmentTransaction.commit();//提交事務
            }
        });
    }

}

7、執行後效果如上所說一樣,此專案的回撥方法都是放在Fragment2碎片中:
(1)、然後開始執行此應用時,Log列印如下:
這裡寫圖片描述
當 Fragment2第一次被載入到螢幕上時,會依次執行 onAttach() 、onCreate() 、onCreateView() 、onActivityCreated() 、onStart() 和 onResume() 方法。
(2)、然後點選Fragment1中的按鈕,如下圖所示:
這裡寫圖片描述
由於Fragment3替換了Fragment2,此時的Fragment2進入了停止狀態,因此 onPause()、onStop()和 onDestroyView()方法會得到執行。當然如果在替換的時候沒有呼叫addToBackStack()方法,此時的 RightFragment 就會進入銷燬狀態,onDestroy()和onDetach()方法就會得到執行。
(3)、接著,按下返回物理鍵,Fragment2會重新回到螢幕,此時列印資訊如下:
這裡寫圖片描述
由於Fragment2重新回到了執行狀態,因此onActivityCreated()、onStart()和 onResume()方法會得到執行。注意此時 onCreate()和 onCreateView()方法並不會執行,因為我們藉助了addToBackStack()方法使得Fragment2和它的檢視並沒有銷燬。
(4)、再次按返回物理鍵,列印資訊如下:
這裡寫圖片描述
依次會執行 onPause()、onStop()、onDestroyView()、onDestroy()和 onDetach()方法,最終將活動和碎片一起銷燬。

五、學習了碎片的生命週期,我們可以在碎片的某種特定狀態下進行我們的邏輯操作,比如,我上面舉的例子,就是在Fragment2重新回到執行狀態,即回撥方法onResume()中進行操作,使得重新回到執行狀態的Fragment2碎片的TextView顯示原來的內容。