Fragment的生命週期與切換
準備重新入手安卓了,依然選擇從fragment開始。衝鴨~!
Fragment有如下兩個選擇
android.support.v4.app.Fragment
android.app.Fragment;
support.v4.app下的片段具有更好的相容性,可以相容到1.6版本,而app下的Fragment只支援3.0以上的版本,如果minsdk設定在3.0以上,當然用哪個都可以,如果需要相容到3.0以下,建議使用V4包下的Fragment。
劃重點1:support.v4.app下的片段可以相容到1.6版本,且support.v4.app下的Fragment和app下的Fragment不具有繼承關係,無法進行型別轉換。
以support.v4.app下的Fragment為例,正式開始今天的學習。
首先使用replace的方式實現fragment的切換。
建立ShowFragment:
ShowFragment.java
public class ShowFragment extends Fragment { private static final String TAG = "ShowFragment"; String mText; public static ShowFragment getInstance(String text){ ShowFragment showFragment = new ShowFragment(); showFragment.mText = text; return showFragment; } @Override public void onAttach(Context context) { super.onAttach(context); Log.d(TAG, "onAttach: " + mText); } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(TAG, "onCreate: " + mText); } @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { Log.d(TAG, "onCreateView: " + mText); return inflater.inflate(R.layout.show_fragment,container,false); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); TextView text = view.findViewById(R.id.tv); text.setText(mText); Log.d(TAG, "onViewCreated: " + mText); } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); Log.d(TAG, "onActivityCreated: " + mText); } @Override public void onStart() { super.onStart(); Log.d(TAG, "onStart: " + mText); } @Override public void onResume() { super.onResume(); Log.d(TAG, "onResume: " + mText); } @Override public void onPause() { super.onPause(); Log.d(TAG, "onPause: " + mText); } @Override public void onStop() { super.onStop(); Log.d(TAG, "onStop: " + mText); } @Override public void onDestroyView() { super.onDestroyView(); Log.d(TAG, "onDestroyView: " + mText); } @Override public void onDestroy() { super.onDestroy(); Log.d(TAG, "onDestroy: " + mText); } @Override public void onDetach() { super.onDetach(); Log.d(TAG, "onDetach: " + mText); } }
show_fragment.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:textSize="20sp" android:textColor="#333333" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:id="@+id/tv"/> </RelativeLayout>
然後建立承載Fragment的Activity:
FragmentShowActivity.java
public class FragmentShowActivity extends AppCompatActivity {
RelativeLayout mRoot;
ShowFragment show1;
ShowFragment show2;
Button btn;
FragmentManager fm;
boolean is1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment_show);
mRoot = findViewById(R.id.rl);
btn = findViewById(R.id.btn);
show1 = ShowFragment.getInstance("show1");
show2 = ShowFragment.getInstance("show2");
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
//*注意此處使用replace替換fragment
if (!is1) {
ft.replace(R.id.fragment, show1);
ft.commit();
} else {
ft.replace(R.id.fragment, show2);
ft.commit();
}
is1 = !is1;
}
});
}
}
activity_fregment_show.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
android:id="@+id/rl"
tools:context=".FragmentShowActivity">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/fragment"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="btn"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="50dp"
android:id="@+id/btn"/>
</RelativeLayout>
Activity中建立了命名為show1和show2的ShowFragment,以及一個控制Fragment替換的按鈕,點選按鈕會替換Fragment。
執行程式,第一次點選按鈕輸出如下(onViewCreated為onCreateView執行完成的回撥方法,不屬於Fragment生命週期)
執行順序為
onAttach(初次繫結時呼叫)
onCreate(建立Fragment時呼叫)
onCreateView(建立Fragment佈局時呼叫)
onActivityCreated(Activity的OnCreate呼叫完成後才會呼叫)
onStart(Fragment轉變為可見狀態)
onResume(Fragment轉變為前臺顯示,可互動狀態)
退回桌面執行
onPause(Fragment變為不可互動狀態)
onStop(Fragment變為不可見狀態)
再次進入程式
onStart
onResume
此時點選按鈕替換為名叫show2的Fragment
替換操作執行多次,會發現都是先執行將要顯示的Fragment的onAttach方法和onCreate方法,然後執行當前Fragment的銷燬系列方法,最後才載入將顯示的Fragment的佈局並顯示。
替換操作時前一個Fragment和後一個Fragment的生命週期有沒有確定的時序關係還需要繼續看原始碼才能確定,暫時不與討論。
但可以確認的一點是 執行一次替換操作時,前一個Fragment會走一個完整的生命週期,即從繫結到解綁到操作。
切換Fragment除了使用replace,還可以使用hide和show。
將FragmentShowActivity的onCreate稍作修改,使用hide和show方法來做fragment的切換,看看現在fragment的生命週期會有何改變。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment_show);
mRoot = findViewById(R.id.rl);
btn = findViewById(R.id.btn);
show1 = ShowFragment.getInstance("show1");
show2 = ShowFragment.getInstance("show2");
fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.fragment, show1);
ft.add(R.id.fragment, show2);
ft.hide(show1);
ft.commit();
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
if (!is1) {
ft.hide(show1);
ft.show(show2);
ft.commit();
} else {
ft.hide(show2);
ft.show(show1);
ft.commit();
}
is1 = !is1;
}
});
}
啟動app,檢視日誌
可以看到show1和show2的生命週期同步了,並且都在呼叫add方法時開始執行。
返回桌面再回app
果然很同步。。點選按鈕切換一下看看回發生什麼
沒錯。。什麼都沒有發生,fragment的生命週期不會因為show和hide而發生改變。
還有一種使用replace+回退棧的方式實現Fragment的切換,使用了回退棧後,Fragment的生命週期又不同了,這一現象將在下一篇文章進行分析,我們可以搬出那張經典的圖,祕密藏在圖裡。