Fragment的入門使用到進階

Fragment是什麼?

碎片(Fragment)是一種可以嵌入在活動中的UI片段,它能讓程序更加合理和充分地利用大屏幕的空間,因而在平板上應用得非常廣泛。
同樣的界面在手機上顯示可能很好看,在平板上就未必了,因爲平板的屏幕非常大,手機的界面放在平板上可能會有過分被拉長、控件間距過大等情況。這個時候更好的體驗效果是在Activity中嵌入"小Activity",然後每個"小Activity"又可以擁有自己的佈局。因此,我們今天的主角Fragment登場了。

Fragment的簡單用法

這裏我們準備先寫一個最簡單的碎片示例來練練手,在一個活動當中添加兩個碎片並讓這兩個碎片平分活動空間。
新建一個左側碎片佈局fragment_a.xml代碼如下:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/blue"
    android:gravity="center"
    >
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/fragment_a"
        android:textSize="18sp"
        android:textStyle="bold"
        android:textColor="@color/mid_night_blue"
        />
</LinearLayout>

這個佈局非常簡單,只放置了一個按鈕,並讓它水平居中顯示。然後新建右側碎片佈局fragment_b.xml,代碼如下所示:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/dark_orange"
    android:gravity="center"
    >
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/fragment_b"
        android:textSize="18sp"
        android:textStyle="bold"
        android:textColor="@color/mid_night_blue"
        />

</LinearLayout>

這裏的顏色和string自己配下,直接在color.xml和string.xml添加你喜歡的顏色就好,這裏我推薦下我走顏色的網址:
顏色選擇器

接着新建一個FragmentA類,並讓它繼承自Fragment。繼承的Fragment是androidx下的。以後建議都用androidx下包的控件。
現在編寫一下FragmentA中的代碼,如下所示:

class FragmentA : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return View.inflate(context, R.layout.fragment_a,null)
    }
}

這裏僅僅是重寫了Fragment的onCreateView()方法,然後在這個方法中通過LayoutInflater的inflate()方法將剛纔定義的left_fragment佈局動態加載進來,整個方法簡單明瞭。接着用同樣的方法建立一個FragmentB,代碼如下:

class FragmentB : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return View.inflate(context, R.layout.fragment_b,null)
    }
}

接下來修改activity_main.xml中的代碼,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <FrameLayout
        android:id="@+id/fragment_a"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@id/fragment_b"
        app:layout_constraintTop_toTopOf="parent"
        />
    <FrameLayout
        android:id="@+id/fragment_b"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        app:layout_constraintLeft_toRightOf="@id/fragment_a"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        />

</androidx.constraintlayout.widget.ConstraintLayout>

看清楚這裏是兩個FrameLayout,不是fragment,通常我們在開發的時候都是動態來打開fragment的,這裏就不介紹靜態方法了,如果想了解,直接在佈局裏面添加兩個fragment佈局就好,id需要對應起來。

接下來還要對MainActivity中進行一番操作:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        var fragmentA = FragmentA()
        var fragmentB = FragmentB()
        supportFragmentManager.beginTransaction().add(R.id.fragment_a,fragmentA).commitAllowingStateLoss()
        supportFragmentManager.beginTransaction().add(R.id.fragment_b,fragmentB).commitAllowingStateLoss()
    }
}

看到效果如下

在這裏插入圖片描述
動態添加碎片主要分爲5步:

(1)創建待添加的碎片示例。

(2)獲取FragmentManager,在活動中可以直接通過getSupportFragmentManager()方法得到。

(3)開啓一個事務,通過調用beginTransaction()方法開啓。

(4)向容器內添加或替換碎片,一般使用add()方法實現,需要傳入容器的id和待添加的碎片實例。

(5)提交事務,調用commit()方法來完成。

這樣就完成了在活動中動態添加碎片的功能。

Fragment的生命週期

class FragmentA : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        Log.e("FragmentCurrentLife", "onCreateView")
        return View.inflate(context, R.layout.fragment_a,null)
    }

    override fun onAttach(context: Context) {
        super.onAttach(context)
        Log.e("FragmentCurrentLife", "onAttach")
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.e("FragmentCurrentLife", "onCreate")
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        Log.e("FragmentCurrentLife", "onActivityCreated")
    }

    override fun onStart() {
        super.onStart()
        Log.e("FragmentCurrentLife", "onStart")
    }

    override fun onResume() {
        super.onResume()
        Log.e("FragmentCurrentLife", "onResume")
    }

    override fun onPause() {
        super.onPause()
        Log.e("FragmentCurrentLife", "onPause")
    }

    override fun onStop() {
        super.onStop()
        Log.e("FragmentCurrentLife", "onStop")
    }

    override fun onDestroyView() {
        super.onDestroyView()
        Log.e("FragmentCurrentLife", "onDestroyView")
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.e("FragmentCurrentLife", "onDestroy")
    }

    override fun onDetach() {
        super.onDetach()
        Log.e("FragmentCurrentLife", "onDetach")
    }

} 

開啓一個fragment
在這裏插入圖片描述
按下home鍵
在這裏插入圖片描述
重回fragment
在這裏插入圖片描述
按下物理回退鍵退出
在這裏插入圖片描述
onAttach():執行該方法時,Fragment與Activity已經完成綁定,該方法有一個Activity類型的參數,代表綁定的Activity,這時候你可以執行諸如mActivity = activity的操作。

onCreate():初始化Fragment。可通過參數savedInstanceState獲取之前保存的值。

onCreateView():初始化Fragment的佈局。加載佈局和findViewById的操作通常在此函數內完成,但是不建議執行耗時的操作,比如讀取數據庫數據列表。

onActivityCreated():執行該方法時,與Fragment綁定的Activity的onCreate方法已經執行完成並返回,在該方法內可以進行與Activity交互的UI操作,所以在該方法之前Activity的onCreate方法並未執行完成,如果提前進行交互操作,會引發空指針異常。

onStart():執行該方法時,Fragment由不可見變爲可見狀態。

onResume():執行該方法時,Fragment處於活動狀態,用戶可與之交互。

onPause():執行該方法時,Fragment處於暫停狀態,但依然可見,用戶不能與之交互。

onSaveInstanceState():保存當前Fragment的狀態。該方法會自動保存Fragment的狀態,比如EditText鍵入的文本,即使Fragment被回收又重新創建,一樣能恢復EditText之前鍵入的文本。

onStop():執行該方法時,Fragment完全不可見。

onDestroyView():銷燬與Fragment有關的視圖,但未與Activity解除綁定,依然可以通過onCreateView方法重新創建視圖。通常在ViewPager+Fragment的方式下會調用此方法。

onDestroy():銷燬Fragment。通常按Back鍵退出或者Fragment被回收時調用此方法。

onDetach():解除與Activity的綁定。在onDestroy方法之後調用。

setUserVisibleHint():設置Fragment可見或者不可見時會調用此方法。在該方法裏面可以通過調用getUserVisibleHint()獲得Fragment的狀態是可見還是不可見的,如果可見則進行懶加載操作。

Fragment執行流程分解:

1、Fragment創建:setUserVisibleHint()->onAttach()->onCreate()->onCreateView()->onActivityCreated()->onStart()->onResume();

2、Fragment變爲不可見狀態(鎖屏、回到桌面、被Activity完全覆蓋):onPause()->onSaveInstanceState()->onStop();

3、Fragment變爲部分可見狀態(打開Dialog樣式的Activity):onPause()->onSaveInstanceState();

4、Fragment由不可見變爲活動狀態:onStart()->OnResume();

5、Fragment由部分可見變爲活動狀態:onResume();

6、Fragment退出:onPause()->onStop()->onDestroyView()->onDestroy()->onDetach()(注意退出不會調用onSaveInstanceState方法,因爲是人爲退出,沒有必要再保存數據);

7、Fragment被回收又重新創建:被回收執行onPause()->onSaveInstanceState()->onStop()->onDestroyView()->onDestroy()->onDetach(),重新創建執行onAttach()->onCreate()->onCreateView()->onActivityCreated()->onStart()->onResume()->setUserVisibleHint();橫豎屏切換:與Fragment被回收又重新創建一樣。

onHiddenChanged的回調時機

當使用add()+show(),hide()跳轉新的Fragment時,舊的Fragment回調onHiddenChanged(),不會回調onStop()等生命週期方法,而新的Fragment在創建時是不會回調onHiddenChanged(),這點要切記。

最後說一下,目前用的很多的一種模式就是 一個Activity對多個Fragment
有一篇文章講的很好
Fragment全解析系列
想深度學習的可以去看看
最後推薦大佬寫的框架:
Fragmentation

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章