Android基礎回顧(三)| 關于Fragment

參考書籍:《第一行代碼》 第二版 郭霖
如有錯漏,請批評指出!

Fragment的簡單使用

  • 靜態添加Fragment

    首先創建一個項目,自動生成MainActivity和它的布局文件activity_main.xml,然后創建三個Fragment,取消掉下面兩項的勾選,僅僅為它自動創建布局文件。

    這里我們需要注意一下,Android中有兩個包下的Fragment,一個是系統內置的android.app.Fragment,另一個是support-v4庫中的android.support.v4.app.Fragment,系統自動為我們創建的Fragment是support-v4包下的,因為它對Android的版本適配更好,因此后面我們使用的所有Fragment都是support-v4包下的。

    將三個Fragment分別命名為TopFragment1、TopFragment2、ButtomFragment,布局文件fragment_top_1、fragment_top_2、fragment_buttom。
    修改布局文件 fragment_top_1.xml:

    <FrameLayout 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:background="@color/colorWhite"
        tools:context="com.laughter.AboutFragment.fragment.TopFragment1">
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="@string/fragment_1"
            android:textAllCaps="false"
            android:textSize="24sp"/>
    </FrameLayout>
    

    修改 fragment_top_2.xml

    <FrameLayout 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:background="@color/colorGray"
        tools:context="com.laughter.AboutFragment.fragment.TopFragment2">
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="@string/fragment_2"
            android:textSize="24sp"
            android:textAllCaps="false"/>
    </FrameLayout>
    

    這些修改只是為了將兩個Fragment區分開來,并沒有什么特別的地方。接下來修改MainActivity的布局文件activity_main.xml

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:baselineAligned="false">
    
        <fragment
            android:id="@+id/top_fragment"
            android:name="com.laughter.AboutFragment.fragment.TopFragment1"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="4"/>
    
        <fragment
            android:id="@+id/buttom_fragment"
            android:name="com.laughter.AboutFragment.fragment.TopFragment2"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"/>
    
    </LinearLayout>
    

    我們可以看到,這里我們在LinearLayout中添加了兩個 <fragment/> 標簽,我們通過android:name屬性為fragment標簽靜態綁定Fragment,這里需要注意一定要寫完整的包名。下面看運行結果:
  • 動態添加Fragment
    相比于靜態添加Fragment,動態添加就要靈活的多了。在前面的基礎上,我們先修改一下MainActivity的布局activity_main.xml(僅僅將第一個<fragment/>標簽改為FrameLayout。):

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:baselineAligned="false">
    
        <FrameLayout
            android:id="@+id/frame_layout"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="4"/>
    
        <fragment
            android:id="@+id/buttom_fragment"
            android:name="com.laughter.AboutFragment.fragment.ButtomFragment"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"/>
    </LinearLayout>
    

    接下來修改ButtomFragment的布局文件fragment_buttom.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="horizontal"
        android:gravity="center"
        tools:ignore="ButtonStyle"
        tools:context="com.laughter.AboutFragment.fragment.ButtomFragment">
    
        <Button
            android:id="@+id/but_left"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="8dp"
            android:layout_margin="8dp"
            android:foreground="?android:attr/selectableItemBackground"
            android:text="@string/frag_1"
            android:textSize="24sp"
            android:textAllCaps="false" />
    
        <Button
            android:id="@+id/but_right"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="8dp"
            android:layout_margin="8dp"
            android:foreground="?android:attr/selectableItemBackground"
            android:text="@string/frag2"
            android:textSize="24sp"
            android:textAllCaps="false"/>
    </LinearLayout>
    

    我們添加了兩個Buttom,用于切換Fragment。最后在MainActivity中實現Fragment的動態添加和切換:

    public class MainActivity extends AppCompatActivity{
    
        private TopFragment1 fragment1;
        private TopFragment2 fragment2;
    
        private final String FRAG1 = "frag1";
        private final String FRAG2 = "frag2";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            ButterKnife.bind(this);
        }
    
        @OnClick({R.id.but_left, R.id.but_right})
        public void show(View view) {
            FragmentManager manager = getSupportFragmentManager();
            FragmentTransaction transaction = manager.beginTransaction();
            hideFragments(manager, transaction);
            switch (view.getId()) {
                case R.id.but_left:
                    if (fragment1 == null){
                        fragment1 = new TopFragment1();
                        transaction.add(R.id.frame_layout, fragment1, FRAG1);
                    }
                    transaction.show(fragment1);
                    break;
                case R.id.but_right:
                    if (fragment2 == null){
                        fragment2 = new TopFragment2();
                        transaction.add(R.id.frame_layout, fragment2, FRAG2);
                    }
                    transaction.show(fragment2);
                    break;
                default:
                    break;
            }
            transaction.commit();
        }
    
        private void hideFragments(FragmentManager manager, FragmentTransaction transaction) {
            fragment1 = (TopFragment1)manager.findFragmentByTag(FRAG1);
            fragment2 = (TopFragment2)manager.findFragmentByTag(FRAG2);
            if (fragment1 != null) {
                transaction.hide(fragment1);
            }
            if (fragment2 != null) {
                transaction.hide(fragment2);
            }
        }
    }
    

    上面代碼中有關于 ButterKnife 的使用,可以參考我的另一篇博客 Android實踐(二) | 注解框架ButterKnife基本使用

    1. 最初,MainActivity的FrameLayout中是什么都沒有的,當我們觸發點擊事件時,我們通過 getSupportFragmentManager() 方法獲取到FragmentManager實例,然后調用它的 beginTransaction() 方法開啟一個事務;
    2. 然后先隱藏所有的Fragment(若事務中已經add了Fragment實例,可以通過 FragmentManager 的 findFragmentByTag() 方法獲取到已經存在的實例,若不存在則返回null);
    3. 接下來判斷Fragment實例是否已經被創建,若未創建則創建實例并通過 FragmentTranscation 的add()方法添加到事務中,這里需要注意 add() 方法有多個重載方法,這里因為我們要使用到TAG這個參數,因此我們使用add(int resId, Fragment fragment, String tag)這個。第一個參數傳入一個layout,也就是將Fragment添加到哪個布局中;第二個參數就是Fragment實例,第三個參數也就是tag,用于標識我們傳入的Fragment實例。
    4. 最后別忘了還要調用 FragmentTranscation 的 commit() 方法提交事務,否則我們前面所做的都無效。

      這樣,我們動態添加Fragment的功能就完成了,看效果:

Fragment的生命周期

和Activity一樣,Fragment也有自己的生命周期,并且它的生命周期和Activity很相似。

為了直觀感受Fragment的生命周期,我們通過一個例子來觀察創建及銷毀一個Fragment完整的生命周期:
還是通過修改前面的例子,我們給重寫Fragmeng的每個生命周期方法,加上打印信息(這里只給出TopFragment1的代碼,TopFragment2一樣):

public class TopFragment1 extends Fragment {

    private final String TAG = "Fragment1";

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

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

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        Log.d(TAG, "onCreateView");
        return inflater.inflate(R.layout.fragment_top1, container, false);
    }

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

    @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, "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");
    }
}

為了生命周期的過程更加清晰,我們用replace()的方式來切換Fragment:

public class MainActivity extends AppCompatActivity {

    private TopFragment1 fragment1;
    private TopFragment2 fragment2;

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

    @OnClick({R.id.but_left, R.id.but_right})
    public void show(View view) {
        FragmentManager manager = getSupportFragmentManager();
        FragmentTransaction transaction = manager.beginTransaction();
        switch (view.getId()) {
            case R.id.but_left:
                if (fragment1 == null) {
                    fragment1 = new TopFragment1();
                }
                transaction.replace(R.id.frame_layout, fragment1);
                break;
            case R.id.but_right:
                if (fragment2 == null){
                    fragment2 = new TopFragment2();
                }
                transaction.replace(R.id.frame_layout, fragment2);
                break;
            default:
                break;
        }
        transaction.commit();
    }
}

補充說明:
Fragment的切換有兩種方式,第一種是add()+show()+hide()的方式,第二種是replace()的方式,關于它們的區別,后面我會寫一篇文章來總結。

下面我們來進行測試:

  1. 點擊Fragment1按鈕,將TopFragment1添加到Activity中,生命周期如下:

    可以看到,Fragment的創建過程比Activity多了幾個方法:
    onAttach():Fragment與Activity建立關聯時調用
    onCreateView():為Fragment加載布局時調用
    onActivityCreated():當Activity的 onCreate() 方法執行完后調用

  2. 然后點擊Fragment2按鈕,將TopFragment2添加到Activity中,生命周期如下:

    當我們切換Fragment時,首先Activity會與Fragment2建立關聯,然后創建Fragment2,接下來暫停、銷毀Fragment1(因為我們使用的是replace()方式切換Fragment,會默認銷毀前一個Fragment的實例),然后為Fragment2加載布局,啟動Fragment2。
    onDestroyView():與onCreateView相對應,Fragment的布局被移除時調用
    onDetach():Activity與Fragment解除關聯時調用

  3. 再切換回Fragment1,又會創建Fragment1的實例,并移除Fragment2:

4 接下來按back鍵退出,會銷毀Fragment1:

根據上面的測試,我們對Fragment的生命周期已經有了清晰的認識了,不過Fragment不獨立存在,一般都是和Activity關聯使用,因此我們還需要了解Fragment與Activity生命周期是如何穿插在一起的。
如果你想熟悉一下Activity的生命周期,可以參考:Android筆記(一) | Activity的生命周期

Activity與Fragment生命周期的關系

在前面的基礎上,我們只需要重寫Activity生命周期的各個方法,并打印出來:

public class MainActivity extends AppCompatActivity {

    private final String TAG = "MainActivity";

    private TopFragment1 fragment1;
    private TopFragment2 fragment2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        Log.d(TAG, "onCreate");
    }

    @OnClick({R.id.but_left, R.id.but_right})
    public void show(View view) { ··· }

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

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

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

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

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

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

接下來再來測試:
為了觀察Activity與Fragment的生命周期是如何交叉在一起的,我們查看靜態加載的ButtomFragment的生命周期(需要重寫ButtomFragment生命周期的的各個方法,并打印,和前面TopFragment一樣),因為靜態加載是和Activity一起創建的。

  1. 創建
  2. 息屏
  3. 亮屏
  4. 進入別的Activity
  5. 再回到MainActivity
  6. 按back鍵退出

有關Fragment的基本知識就是這些了,在實際項目中Fragment的使用十分靈活,還得自己慢慢摸索。


上一篇:Android基礎回顧(二)| 常用控件 — ListView和RecyclerView
下一篇:Android基礎回顧(四)| 關于廣播機制


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,559評論 25 708
  • 用兩張圖告訴你,為什么你的 App 會卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 12,975評論 2 59
  • 動態創建fragment的流程 1.0 新建一個類繼承fragment. 2.0 在自定義的fragment里面復...
    左神話閱讀 2,236評論 0 3
  • 感激者:余俊娟 地點:湖北省武漢市 時間:2017.7.18,周二 1,我感激,早上快7點出門,還有位置! 2,我...
    余俊娟閱讀 235評論 0 0
  • 今天突然想起昨天在超市發生的一些小事,覺得很有趣就記下來。 昨天帶女兒回娘家,固定節目之一就是逛超市。作為一個小村...
    童亨書齋閱讀 187評論 0 0