總結
參考:https://www.jianshu.com/p/28ca4cbe190c
參考:https://www.jianshu.com/p/11c8ced79193
1.生命週期
參考:https://blog.csdn.net/gyh790005156/article/details/79487576
2.Fragment實現原理和Back Stack
參考:https://www.jianshu.com/p/28ca4cbe190c
參考:https://www.jianshu.com/p/11c8ced79193
面試題
1. Fragment爲什麼被稱爲第五大組件
Fragment比Activity更節省內存,其切換模式也更加舒適,使用頻率不低於四大組件,且有自己的生命週期,並且必須依付於Activity
2. Activity創建Fragment的方式
- 靜態創建具體步驟
首先我們同樣需要註冊一個xml文件,然後創建與之對應的java文件,通過onCreatView()的返回方法進行關聯,最後我們需要在Activity中進行配置相關參數即在Activity的xml文件中放上fragment的位置。
- 動態創建具體步驟
(1)創建待添加的碎片實例
(2)獲取FragmentManager,在活動中可以直接通過調用 getSupportFragmentManager()方法得到。
(3)開啓一個事務,通過調用beginTransaction()方法開啓。
(4)向容器內添加或替換碎片,一般使用repalce()方法實現,需要傳入容器的id和待添加的碎片實例。
(5)提交事務,調用commit()方法來完成。
3. FragmentPageAdapter和FragmentPageStateAdapter的區別
- FragmentPagerAdapter:
適用於頁面較少的情況,後者適用於頁面較多的情況,通過源碼瞭解,主要查看destroyItem方法中的最後一行,mcurtransaction.remove(fragment),通過這行代碼瞭解到,FragmentStatePagerAdapter是真正釋放fragment內存,
- FragmentStatePagerAdapter
在FragmentPagerAdapter的destroyItem方法中所調用的是mcurtransaction.detach(fragment),他僅僅是將fragment的頁面與activity的頁面抽離開來,並沒有真正的銷燬fragment釋放內存.
4. Fragment的生命週期
5. Fragment通信
- Fragment與Activity通信方式
1.在fragment中調用activity中的方法getActivity
2.在Activity中條用Fragment中的方法,一般常用的是接口回調,在fragment中創建接口,在activity中實現接口,這樣就能完成activity中調用fragment中的方法
3.在Fragment中調用Fragment中的方法,首先先通過getactivity方法,獲取activity的方法,然後通過fingFragmentById獲取到另外一個fragment的方法,然後進行調用
6. ViewPager與Fragment結合使用時的懶加載問題
- 所謂的 “懶加載” 就是數據只有在Fragment對於用戶可見的時才進行加載。因爲ViewPager會幫我們預先初始化Fragment。由於這個特性,我們不能把數據的加載放到onCreateView方法或者onCreate方法中。
因此,我們需要判定Fragment在什麼時候是處於可見的狀態。一般我們通常是通過Fragment中的生命週期方法onResume來判斷Fragment是否可見,但是由於ViewPager預加載的特性,Fragment即便不可見也會執行onResume方法,因此使用這個方法進行可見性的判斷就行不通了。這個時候我們需要用到下面的這個方法來進行Fragment可見性的判斷:
setUserVisibleHint()方法:
什麼時候被調用?
- 當fragment被創建的時,setUserVisibleHint(boolean isVisibleToUser)方法會被調用,且傳入參數值爲false。
- 當fragment可見時,setUserVisibleHint(boolean isVisibleToUser)方法會被調用,且傳入參數值爲true。
- 當fragment由 可見 -> 不可見 時,setUserVisibleHint(boolean isVisibleToUser)方法會被調用,且傳入參數值爲false。
只需要當setUserVisibleHint(boolean isVisibleToUser)方法中的 isVisibleToUser 參數的值爲true的時候我們纔開始進行數據的加載
注意事項:
setUserVisibleHint(boolean isVisibleToUser)方法在Fragment的生命週期方法onCreate 之前調用的,也就是說他並不在Fragment的生命週期中。既然是在 onCreate 方法之前被調用,這樣就存在許多不確定因素,如果Fragmnet的View還沒有完成初始化之前,就在setUserVisibleHint()方法中進行UI的操作,這樣顯然會導致空指針的出現
解決方法:
我們需要對Fragment創建的View進行緩存,確保緩存的View不爲空的情況下我們纔可以在setUserVisibleHint方法中進行UI操作。
/**
* Fragment的基類(懶加載)
* Created by wangke on 17-8-15.
*/
public abstract class BaseLazyFragment extends RxFragment {
private String TAG;
//標記當前Fragment的可見狀態
private boolean isFragmentVisible;
private boolean isFirstVisible;
//對Fragment中加載的View進行緩存
private View mRootView;
private Unbinder mUnbinder;
public BaseLazyFragment(String TAG) {
this.TAG = TAG;
}
/**
* 當Fragment添加到Activity的時候最先調用此方法
*
* @param context 上下文對象
*/
@Override
public void onAttach(Context context) {
super.onAttach(context);
//獲取Fragment之間傳遞過來的參數
initArgs(getArguments());
}
/**
* 獲取Fragment中傳遞過來的參數,選擇重寫
*
* @param arguments
*/
protected void initArgs(Bundle arguments) {
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
LogHelper.i("wwk", TAG + " ===>onCreate");
super.onCreate(savedInstanceState);
initVariable();
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(getContentLayoutId(), container, false);
mUnbinder = ButterKnife.bind(this, view);
initWidget(mRootView);
initEvent();
return view;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
LogHelper.i("wwk", TAG + " ===>onViewCreated");
if (mRootView == null) {
mRootView = view;
if (getUserVisibleHint()) {
if (isFirstVisible) {
//處於可見狀態並且Fragment是第一次開啓
onFragmentFirstVisible();
isFirstVisible = false;
}
onFragmentVisibleChange(true);
isFragmentVisible = true;
}
}
//直接使用緩存的mRootView
super.onViewCreated(mRootView, savedInstanceState);
}
/**
* 初始化控件事件,選擇重寫
*/
private void initEvent() {
}
/**
* 初始化組件
*
* @param view
*/
protected abstract void initWidget(View view);
/**
* 當Fragment第一次創建的時候調用,如果不可見則isVisible的參數值爲false
* 當Fragment對於用戶可見時調用,此時的isVisibleToUser參數值爲true
* 當Fragment當前的狀態由可見變爲不可見時調用,此時的isVisibleToUser參數爲false
*
* @param isVisibleToUser true : 可見
* false : 不可見
*/
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
LogHelper.i("tag", TAG + " ==> setUserVisibleHint ");
super.setUserVisibleHint(isVisibleToUser);
//setUserVisibleHint()的調用在Fragment的聲明週期外調用,需要保證rootView不爲空的時候調用 onFragmentVisibleChange方法
if (mRootView == null) {
return;
}
//第一次被開啓,並且對用戶可見(這個if語句中的判斷一般不會被執行到,setUserVisibleHint方法的調用時mRootView還沒有被緩存)
if (isFirstVisible && isVisibleToUser) {
//當Fragment第一次可見的時候調用
onFragmentFirstVisible();
isFirstVisible = false;
}
//Fragment對於用戶可見(已經不是第一次開啓)
if (isVisibleToUser) {
onFragmentVisibleChange(true);
isFragmentVisible = true;
return;
}
//能執行到這裏表明 Fragment對於用戶已經處於不可見的狀態
if (isFragmentVisible) {
//由 可見 -> 不可見
isFragmentVisible = false;
onFragmentVisibleChange(false);
}
}
/**
* 獲取填充Fragment的View的id
*
* @return
*/
protected abstract int getContentLayoutId();
/**
* 當Fragment的狀態發生變化的時候調用,用於進行數據的刷新
*
* @param isVisible true 不可見 -> 可見
* false 可見 -> 不可見
*/
protected abstract void onFragmentVisibleChange(boolean isVisible);
/**
* 當Fragment第一次被開啓的時候調用,用於請求數據
*/
protected abstract void onFragmentFirstVisible();
/**
* 獲取當前Fragment的可見狀態
*
* @return
*/
protected boolean isFragmentVisible() {
return isFragmentVisible;
}
/**
* 給變量賦初始值
*/
private void initVariable() {
//標記是否是第一次開啓當前Fragment
isFirstVisible = true;
//標記Fragment對於用戶是否可見
isFragmentVisible = false;
//緩存Fragment創建出來的View
mRootView = null;
}
@Override
public void onDestroy() {
initVariable();
mUnbinder.unbind();
super.onDestroy();
}
}