Android Fragment詳解

該文章是一個系列文章,是本人在Android開發的漫漫長途上的一點感想和記錄,我會盡可能按照先易後難的順序進行編寫該系列。該系列引用了《Android開發藝術探索》以及《深刻理解Android 卷Ⅰ,Ⅱ,Ⅲ》中的相關知識,另外也借鑑了其餘的優質博客,在此向各位大神表示感謝,膜拜!!!android


前言

上一篇博客咱們主要總結了以前博客的知識,那麼本篇咱們來分析一下Fragment。Fragment又被稱爲「碎片」,可把它看作是一個輕量的Activity,它能夠像Activity同樣包含佈局。Fragment一般是嵌套在Activity中使用的。app

Fragment初探

Fragment設計之初是也許是爲了適配平板等大屏幕設備,在這些設備上有足夠的空間同時顯示兩個「Activity」,使用Fragment可讓咱們更加充分地利用平板的屏幕空間。面咱們一塊兒來探究下如何使用Fragment。ide

首先須要注意,Fragment是在3.0版本引入的,若是你使用的是3.0以前的系統,須要先加入android-support-v4支持才能使用Fragment功能。在Android Studio中這是很容易的,另請注意儘可能不要用app包中的fragment,由於這個是在3.0以後纔有的,支持的版本過高,在低版本中是是用不了的。函數

[app/build.gradle]佈局

compile 'com.android.support:support-v4:25.3.1'

注:後面的25.3.1是android-support-v4的版本號,25表示android-support-v4的主版本,這個主版本儘可能要與compileSdkVersion 25保持一致。性能

咱們在TestApplication中新建一個包fragment專門用來測試與Fragment相關知識。
測試

新建佈局文件fragment_good.xmlgradle

<FrameLayout
    android:background="@color/light_green"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Goods"
        android:textStyle="bold"
        android:textSize="30sp"
        android:layout_gravity="center"/>
</FrameLayout>

這個佈局文件十分簡單,僅僅是在FrameLayout佈局中間位置放置了一個TextView,注意這裏的FrameLayout是一個佈局,稱爲「幀佈局」,,當咱們往裏面添加控件的時候,會默認把他們放到這塊區域的左上角,而這種佈局方式卻沒有任何的定位方式,因此它應用的場景並很少;幀佈局的大小由控件中最大的子控件決定,若是控件的大小同樣大的話,那麼同一時刻就只能看到最上面的那個組件!後續添加的控件會覆蓋前一個!雖然默認會將控件放置在左上角,可是咱們也能夠經過layout_gravity屬性,指定到其餘的位置!ui

再新建佈局文件fragment_goodcar.xmlspa

<FrameLayout
    android:background="@color/light_green"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="GoodCar"
        android:textStyle="bold"
        android:textSize="30sp"
        android:layout_gravity="center"/>
</FrameLayout>

這個佈局文件與上面的幾乎徹底一致。

那麼咱們的佈局文件有了,咱們該新建使用該佈局文件的類了,這一點與Activity能夠說是徹底一致

新建GoodsFragment,該類繼承於Fragment

public class GoodsFragment extends Fragment {
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        //加載上面的佈局文件
        View view = inflater.inflate(R.layout.fragment_goods, null);

        return view;
    }
}

能夠看到Fragment的使用幾乎與Activity一模一樣。下面如法炮製新建GoodCarFragment

public class GoodCarFragment extends Fragment {

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        //加載上面的佈局文件
        View view = inflater.inflate(R.layout.fragment_goodcar, null);

        return view;
    }
}

注:其實在AndroidStudio中有更簡單的辦法

Fragment有了,那麼怎麼使用呢,Fragment通常是嵌套在Activity中使用,那麼咱們新建一個EasyFragmentActivity

在其佈局文件中activity_easy_fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<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:baselineAligned="false" >
    <!--在這裏使用fragment 標籤 並指定name爲上面定義的兩個Fragment的全類名-->

    <fragment
        android:id="@+id/fragment1"
        android:name="com.mafeibiao.testapplication.fragment.GoodCarFragment"
        android:layout_width="0dip"
        android:layout_height="match_parent"
        android:layout_weight="1"
        tools:layout="@layout/fragment_goodcar" />

    <fragment
        android:id="@+id/fragment2"
        android:name="com.mafeibiao.testapplication.fragment.GoodsFragment"
        android:layout_width="0dip"
        android:layout_height="match_parent"
        android:layout_weight="1"
        tools:layout="@layout/fragment_goods" />

</LinearLayout>

EasyFragmentActivity的代碼也極其簡單,使用默認生成的就能夠了

public class EasyFragmentActivity extends AppCompatActivity {

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

到這裏咱們運行程序就可獲得以下結果

到這裏咱們已經會使用Fragment了,神奇不神奇?不過上面的只是最簡單的,有的文章稱它是靜態的使用Fragment,由於咱們只是在XML中引用了一下。既然有靜態,那就有動態了,下面來看動態

動態添加Fragment

稍微修改一下上面的佈局文件activity_easy_fragment

<?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:baselineAligned="false"
    android:orientation="vertical">
    
    <!--刪除了上面兩個fragment標籤,加入了一個id爲main_layout的FrameLayout佈局-->
    <FrameLayout
        android:id="@+id/main_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

咱們的Activity代碼也作了相應改變

public class EasyFragmentActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_easy_fragment);
        Display display = getWindowManager().getDefaultDisplay();
        if (display.getWidth() > display.getHeight()) {
            GoodCarFragment fragment1 = new GoodCarFragment();
            getSupportFragmentManager().beginTransaction().replace(R.id.main_layout, fragment1).commit();
        } else {
            GoodsFragment fragment2 = new GoodsFragment();
            getSupportFragmentManager().beginTransaction().replace(R.id.main_layout, fragment2).commit();
        }
    }
}

首先,咱們要獲取屏幕的寬度和高度,而後進行判斷,若是屏幕寬度大於高度就添加fragment1,若是高度大於寬度就添加fragment2。動態添加Fragment主要分爲4步:

  1. 獲取到FragmentManager,在Activity中能夠直接經過getSupportFragmentManager獲得。在這裏不推薦使用getFragmentManager獲得FragmentManager,而使用v4包下getSupportFragmentManager。
  2. 開啓一個事務,經過調用beginTransaction方法開啓。
  3. 向容器內加入Fragment,通常使用replace方法實現,須要傳入容器的id和Fragment的實例。replace方法有幾個重載方法,在這裏使用的是

    FragmentTransaction replace(@IdRes int containerViewId, Fragment fragment);

    該方法第1個參數是容器ID,即容納Fragment的容器ID,這裏傳入的是在activity_easy_fragment聲明的id爲main_layout的FrameLayout佈局,第2個參數即自定義的Fragment對象。整個函數的意思就是第2個參數中指定的Fragment嵌入到第一個參數指定的佈局容器中。這一點咱們能夠經過Hierarchy View驗證。

  4. 提交事務,調用commit方法提交。

Fragment生命週期

和Activity同樣,Fragment 也有本身的生命週期,理解Fragment的生命週期很是重要,咱們經過代碼的方式來瞧一瞧Fragment的生命週期是什麼樣的:

public class GoodsFragment extends Fragment {
    private static String TAG= GoodsFragment.class.getSimpleName();
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        Log.d(TAG,"onAttach");
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Log.d(TAG,"onCreateView");
        View view = inflater.inflate(R.layout.fragment_goods, null);

        return view;
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        Log.d(TAG,"onViewCreated");
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        Log.d(TAG,"onActivityCreated");
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG,"onCreate");
    }

    @Override
    public void onStart() {
        super.onStart();
        Log.d(TAG,"onStart");
    }

    @Override
    public void onResume() {
        super.onResume();
        Log.d(TAG,"onResume");
    }

    @Override
    public void onPause() {
        super.onPause();
        Log.d(TAG,"onPause");
    }

    @Override
    public void onStop() {
        super.onStop();
        Log.d(TAG,"onStop");
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        Log.d(TAG,"onDestroyView");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG,"onDestroy");
    }

    @Override
    public void onDetach() {
        super.onDetach();
        Log.d(TAG,"onDetach");
    }
}

個人神呀,Fragment的生命週期函數真是多呀。Fragment的生命週期函數比Activity的生命週期函數要多很多。不過要理解Fragment的生命週期並不難。由於咱們前面的文章中已經分析了Activity的生命週期。把EasyFragmentActivity也稍微修改一下,加入生命週期函數

public class EasyFragmentActivity extends AppCompatActivity {
    private static String TAG= EasyFragmentActivity.class.getSimpleName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG,"onCreate");
        setContentView(R.layout.activity_easy_fragment);
        Display display = getWindowManager().getDefaultDisplay();
        if (display.getWidth() > display.getHeight()) {
            GoodCarFragment fragment1 = new GoodCarFragment();
            getSupportFragmentManager().beginTransaction().replace(R.id.main_layout, fragment1,Constant.GOODCAR_FRAGMENT_FLAG).commit();
        } else {
            GoodsFragment fragment2 = new GoodsFragment();
            getSupportFragmentManager().beginTransaction().replace(R.id.main_layout, fragment2,Constant.GOODS_FRAGMENT_FLAG).commit();
        }
    }

    @Override
    public void onStart() {
        super.onStart();
        Log.d(TAG,"onStart");
    }

    @Override
    public void onResume() {
        super.onResume();
        Log.d(TAG,"onResume");
    }

    @Override
    public void onPause() {
        super.onPause();
        Log.d(TAG,"onPause");
    }

    @Override
    public void onStop() {
        super.onStop();
        Log.d(TAG,"onStop");
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG,"onDestroy");
    }
}

運行一下

咱們在EasyFragmentActivity中onCreate函數中動態加載了Fragment,看Fragment的生命週期的回調順序,咱們來解釋一下

  • onAttach方法:Fragment和Activity創建關聯的時候調用。
  • onCreate方法:與Activity同樣,Fragment被建立的的時候調用。
  • onCreateView方法:爲Fragment加載佈局時調用。
  • onActivityCreated方法:當Activity中的onCreate方法執行完後調用。
  • onstart方法: 與Activity同樣,Fragment開始可見但不可交互的時候調用。
  • onResume方法:與Activity同樣,是當該Fragment與用戶能進行交互時被執行,用戶能夠得到Fragment的焦點,可以與用戶交互。

按下home鍵以後

  • onPause方法 與Activity同樣,一般是當前的Fragment被暫停了。
  • onStop方法 與Activity同樣,通常是用戶按下了home鍵,Fragment變爲後臺後,以後用戶再切換回這個Fragment就會調用onRestart()然後調用onStart()

按下back鍵以後

  • onDestroyView方法:Fragment中的佈局被移除時調用。
  • onDetach方法:Fragment和Activity解除關聯的時候調用。

Fragment的生命週期與Activity的生命週期很相似,Fragment一般也是嵌套在Activity中使用,因此的Fragment的生命週期跟當前Activity的狀態息息相關。

Fragment通訊

一個宿主Activity可能包含2個以上的Fragment,那麼Fragment之間以及Fragment與宿主Activity之間的通訊問題是咱們不得不解決的問題。

總結來講,基本有如下幾種方法:

Fragment直接調用Activity中的public方法

咱們能夠直接在Fragment中調用Activity中的公開方法,以下:

public class GoodsFragment extends Fragment {
    
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Log.d(TAG,"onCreateView");
        View view = inflater.inflate(R.layout.fragment_goods, null);
        //經過getActivity()獲取與該Fragment相關聯的Activity
        ((EasyFragmentActivity)getActivity()).show();
        return view;
    }
}

show方法是定義在EasyFragmentActivity中的public方法

public class EasyFragmentActivity extends AppCompatActivity {
    public void show(){
        Log.d(TAG,"show");
    }
}

直接在一個Fragment中調用另一個Fragment中的方法

咱們能夠直接在一個Fragment中調用另一個Fragment的公開方法,前提是要先拿到另一個Fragment的實例,咱們先來看看怎樣拿到實例:

public class GoodsFragment extends Fragment {
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Log.d(TAG,"onCreateView");
        View view = inflater.inflate(R.layout.fragment_goods, null);
        //        ((EasyFragmentActivity)getActivity()).show();
        GoodCarFragment goodCarFragment = (GoodCarFragment) getActivity().getSupportFragmentManager().findFragmentByTag("GOODCAR_FRAGMENT_FLAG");
        goodCarFragment.show();
        return view;
    }
}

上面的findFragmentByTag是FragmentManager類的方法,是根據Tag找到對應的Fragment,這個Tag是在EasyFragmentActivity中添加Fragment時指定的。

使用接口(推薦使用)

先定義一個接口,咱們在接口中定義咱們須要的功能

public interface IShow {
    public void show();
}

而後讓宿主Activity實現該接口

public class EasyFragmentActivity extends AppCompatActivity implements IShow {
     @Override
    public void show(){
        Log.d(TAG,"show");
    }
}

而後在Fragment中定義一個該接口的成員變量,並在Fragment onAttach方法中實例化該接口

public class GoodsFragment extends Fragment {
    
    private IShow mCallback;
    
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context != null) {
            mCallback = (IShow) context;
        }
        Log.d(TAG,"onAttach");
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Log.d(TAG,"onCreateView");
        View view = inflater.inflate(R.layout.fragment_goods, null);
        //而後在這裏就可使用了
        mCallback.show();
        return view;
    }
}

這種方案應該是既能達到Fragment複用,又能達到很好的可維護性,而且性能也是槓槓的,因此說推薦使用。

其餘方案

在上面的方案以外,還存在Hanler、廣播等通用的通訊解決方案,我這裏就不進行具體介紹了。由於前面咱們已經詳細分析了Handler的消息機制,至於廣播,我會在之後的文章中分析。


本篇總結

咱們本篇詳細分析了Android Fragment相關知識,靈活運用Fragment可讓你獲得意向不到的效果,咱們使用Fragment下降耦合度,可是可能也增長了Fragment之間以及Fragment與宿主Activity之間的通訊成本,不過這點成本對於程序的易擴展性來講是十分值得的。

參考文章:
http://blog.csdn.net/guolin_blog/article/details/8881711


下篇預告

下一篇文章是呢是Fragment的最佳實踐,讀者敬請期待哦。


此致,敬禮

相關文章
相關標籤/搜索