1. 程式人生 > >自學Android之UI元件:(二)Fragment的基本使用(上)

自學Android之UI元件:(二)Fragment的基本使用(上)

本篇為UI元件的第二篇,主要探討關於Fragment的基本使用,包括簡單的原理以及建立、修改、刪除等操作。

在本篇文章中,你將瞭解到:
1.什麼是Fragment。
2.Fragment的生命週期。
3.Fragment的基本使用。

什麼是Fragment?

由於Android是一款開源的系統,在國內外不同的廠商研發出了很多不同的機型,甚至將Android應用到了平板中,但是這意味著不同廠商出產的手機、平板的螢幕大小不一,例如,元件排版安裝5.0寸手機制作的程式,如果在4.7寸的手機上,視覺效果體驗雖說會降低,但也不至於不可接受。但是如果放到6.0寸的手機,乃至平板上,使用者的視覺體驗會大幅度降低。
這就導致了Android研發的工作中有比較大的比重的解析度匹配,而Fragment則是用來解決這一問題的。
這是因為,Fragment是依賴於Activity的,在Activity的佈局中建立一個FrameLayout元件,就可以在上面根據不同的需要顯示很多個Fragment(當然不能同時顯示)。並且Fragment有自己的生命週期、自己的佈局檔案、獨立的事件處理,極端的說,你甚至可以把一個Fragment當做一個Activity來使用。

Fragment的生命週期

前面說過,Fragment是依賴於Activity的,即必須先建立Activity,再建立Fragment,並且Fragment的生命週期細節上同Activity所有不同,但仍是可以對應參考的。先來看一下Google官方給出的Fragment與Activity生命週期對照表:
這裡寫圖片描述
我們現在來簡單解釋一下Fragment多出來的生命週期:
onAttach():
當Fragment與Activity發生關聯時呼叫該方法。
onCreateView():
在Fragment中初始化Layout、初始化元件時使用。
onActivityCreated():


當Activity的onCreate()返回時呼叫。
onDestoryView():
與onCreateView想對應,當該Fragment的檢視被移除時呼叫。
onDetach():
與onAttach相對應,當Fragment與Activity關聯被取消時呼叫。
另外,值得注意的是,除了onCreateView()外,如果你重寫了其他方法,則必須呼叫spue使父類來實現該方法。

準備工作:

本案例中計劃使用動態新增Fragment的方式,來分別在同一個FrameLayout元件中分別顯示兩個不同的Fragment,並且完成替換、刪除、在Fragment中修改Activity的內容,以及在Activity中修改Fragment的內容等操作。
首先來看一下效果圖:

這裡寫圖片描述

接著開始進行一些初始化等準備工作,程式碼如下:
以下程式碼在activity_main中寫入:

<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:layout_width="match_parent"
        android:layout_height="30dp"
        android:layout_gravity="center_vertical"
        android:text="這裡是MainActivity"
        android:textSize="18sp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:orientation="horizontal">

        <Button
            android:id="@+id/ButtonShowFragmentNow"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="顯示FragmentNow"
            android:textAllCaps="false" />

        <Button
            android:id="@+id/ButtonShowFragmentTwo"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="顯示FragmentTwo"
            android:textAllCaps="false" />
    </LinearLayout>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:orientation="horizontal">

        <Button
            android:id="@+id/ButtonAmendFragmentTwo"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="修改FragmentTwo"
            android:textAllCaps="false" />

        <Button
            android:id="@+id/ButtonReplaceFragmentNow"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="替換" />

        <Button
            android:id="@+id/ButtonDeleteFragmentNow"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="刪除FragmentNow"
            android:textAllCaps="false" />
    </LinearLayout>

    <TextView
        android:id="@+id/TextView"
        android:layout_width="match_parent"
        android:layout_height="20dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginBottom="5dp"
        android:text="這就是用來被Fragment修改的"
        android:textAllCaps="false"
        android:textSize="18sp" />

    <FrameLayout
        android:id="@+id/FrameLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#D02090" />

</LinearLayout>

值得注意的是,在FrameLayout中添加了背景顏色,如果沒有顯示Fragment,則顯示背景顏色,如果添加了Fragment,則顯示Fragment佈局檔案中定義的顏色。

以下程式碼在fragment_now中寫入:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#9aff9a">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/ButtonFragmentNow"
        android:text="修改Activity"
        android:textAllCaps="false"
        android:layout_gravity="center"/>

</LinearLayout>

以下程式碼在fragment_two中寫入:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#d1eeee"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/TextVIewFragmentTwo"
        android:text="註定被修改"
        android:layout_gravity="center"
        android:textSize="30sp"/>

</LinearLayout>

開始建立Fragment:
在上面的準備工作中,建立了一個FrameLayout元件,而Fragment就會顯示在該元件中。另外建立了兩個佈局檔案,這兩個佈局檔案就分別對應兩個不同的Fragment。以下首先來看程式碼思路:

1.首先定義FragmentNow以及FragmentTwo。。
2.接著初始化所需的元件,並設定Button的監聽器
3.通過完整的程式碼顯示FragmentNow。
4.通過簡略的程式碼顯示fragmentTwo。
5.設定Fragment直接的自由替換。
6.一些其他的應用說明。

以下程式碼分別在FragmentNow/Two中寫入:

/* 程式碼執行1:
* 1.在建立Fragment時,需要繼承自v4.app.Fragment中的Fragment。
* 2.然後建立View物件,使用者獲取Fragment所需的Layout。
* 3.onCreateView是用於初始化Fragment的檢視。
* 4.Amend()用於在Activity中呼叫該方法,來修改Fragment中的內容。*/
public class FragmentTwo extends Fragment {
    private View mView;
    private TextView mTextView;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        mView = inflater.inflate(R.layout.fragment_two, null);
        return mView;
    }

    public void Amend(){
        mTextView = (TextView) mView.findViewById(R.id.TextVIewFragmentTwo);
        mTextView.setText("這是FragmentTwo中已經被修改了的值");
    }
}
public class FragmentNow extends Fragment {
    private View mView;
    private Button mButton;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        mView = inflater.inflate(R.layout.fragment_now, null);
        mButton = (Button) mView.findViewById(R.id.ButtonFragmentNow);

        return mView;
    }

    /* 程式碼執行1:
    * 1.1.由於初始化檢視必須在onCreateView()中完成,如果向Activity一樣在onCreate()中呼叫Button是不可以的。
    * 1.2.這是因為onCreate()在onCreateView()之前就被呼叫,如果此時在onCreate()中呼叫Button,會顯示空指標。
    * */
    @Override
    public void onStart() {
        super.onStart();
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                MainActivity mainActivity = (MainActivity) getActivity();
                mainActivity.Amend();
            }
        });
    }
}

以下程式碼在MainActivity中寫入:

    /* 程式碼執行2:
    * 1.首先將兩個Fragment類建立為物件。用於通過該物件來配合FrameLayout顯示fragment。
    * 2.然後獲得各個Button的ID。
    * 3.定義一個boolean型別的變數,用於替換時的迴圈。
    * 4.注意別忘了在onCreate()中呼叫init()。*/
    private void init() {
        mFragmentNow = new FragmentNow();
        mFragmentTwo = new FragmentTwo();
        mButtonShowFragmentNow = (Button) findViewById(R.id.ButtonShowFragmentNow);
        mButtonShowFragmentTwo = (Button) findViewById(R.id.ButtonShowFragmentTwo);
        mButtonAmendFragmentTwo = (Button) findViewById(R.id.ButtonAmendFragmentTwo);
        mButtonReplaceFragmentNow = (Button) findViewById(R.id.ButtonReplaceFragmentNow);
        mButtonDeleteFragmentNow = (Button) findViewById(R.id.ButtonDeleteFragmentNow);
        mBoolean = true;
    }

以下程式碼在MainActivity的onCreate()中寫入:

mButtonShowFragmentNow.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                /* 程式碼執行3:
                * 1.1.首先通過Fragment管理器建立一個Fragment物件,
                * 1.2.通過getSupportFragmentManager來返回同Activity進行關聯並互動的Fragment物件。
                * 2.再將已經建立好的Fragment放置到Fragment事務佇列中。等待Activity的處理。
                * 3.告知該Fragment事務它的顯示位置(在哪個元件上顯示),而Fragment所需的邏輯程式碼在哪個類中(通過物件引用)。
                * 4.這時將Fragment正式提交到Fragment佇列中,使Activity進行處理。*/
                FragmentManager fragmentManager = getSupportFragmentManager();
                FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
                fragmentTransaction.replace(R.id.FrameLayout, mFragmentNow);
                fragmentTransaction.commit();
            }
        });

以下程式碼在MainActivity的onCreate()中寫入:

 mButtonShowFragmentTwo.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                /* 程式碼執行4:
                * 1.使用精簡的方式建立Fragment。並且不使用.add,因為使用.add建立當用戶連續點選後,程式將結束執行。
                * */
                getSupportFragmentManager().beginTransaction()
                        .replace(R.id.FrameLayout, mFragmentTwo).commit();
            }
        });

以下程式碼在MainActivity的onCreate()中寫入:

mButtonReplaceFragmentNow.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                /* 程式碼執行5:
                * 1.替換Activity。由於只有一個Fragment顯示位置,所以只能同時顯示一個。
                * 2.每次替換完成後,則修改mBoolean的值,防止陷入死迴圈。
                * 3.但以上方式會存在一點瑕疵:噹噹前顯示的是mFragment1(預設為真,第一次替換mFragment1)時,第一次點選將(無效果),需要點選兩次,反之同理。
                * */
                if (mBoolean) {
                    getSupportFragmentManager().beginTransaction()
                            .replace(R.id.FrameLayout, mFragmentNow).commit();
                    mBoolean = false;
                } else {
                    getSupportFragmentManager().beginTransaction()
                            .replace(R.id.FrameLayout, mFragmentTwo).commit();
                    mBoolean = true;
                }
            }
        });

以下程式碼在MainActivity的onCreate()中寫入:

        mButtonAmendFragmentTwo.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                /* 程式碼執行6:
                * 1.在Activity中通過Fragment物件呼叫其中的方法。*/
                mFragmentTwo.Amend();
            }
        });
        mButtonDeleteFragmentNow.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                /* 程式碼執行6:
                * 1.如果你希望刪除Fragment,則使用remove方法即可,通過將Fragment物件作為引數。。*/
                getSupportFragmentManager().beginTransaction()
                        .remove(mFragmentNow).commit();
            }
        });

以下程式碼在MainActivity中寫入:

    /* 程式碼執行6:
    * 1.同理,在Activity中建立公共方法,用於Fragment中呼叫。*/
    public void Amend() {
        TextView textView = (TextView) findViewById(R.id.TextView);
        textView.setText("你看吧,Activity中的TextView已經被修改了。");
    }

至此,Fragment中的基本使用你已經學會了,其實Fragment還有一種靜態新增的方式,不過個人認為此種方式使用不夠靈活,且也非常簡單,便沒有在文章中寫出,感興趣的話可以檢視相關文章。
現在再來看一下效果圖,你的效果是否一致?
這裡寫圖片描述

用於操作Fragment的其他方法:

比較常用的操作Fragment的方法在前面的案例中已經有說明了,不過還有一些沒有使用到的需要嘮叨兩句。

.add()
該方法是用於建立Fragment的,但是當你建立過一次Fragment後,再點選一次,則會丟擲異常,不能建立第二次。所以Demo中就沒有使用這種方式。

.replace()
replace()其實是先刪除,再建立,如果不考慮Fragment中的臨時資料的話,可以使用這種方式來建立Fragment。

.hide()
這個方法僅僅是將Fragment隱藏起來,但並不是將其銷燬。

.show()
同樣的,是將隱藏起來的Fragment顯示出來。

後記:

至此本篇結束,不過僅僅是介紹了Fragment最基本的使用,要知道Fragment在實際使用的比例是非常大的,所以也會用比較多的篇幅來說明Fragment。