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