目錄
- Fragment生命周期
-
- 重要方法
- 學以緻用
- Fragment懶加載
-
-
- 懶加載核心
- 學以緻用
-
- Fragment與Activity互動
-
- Fragment切換
- Fragment傳值
-
- Activity向Fragment傳值
-
- setArguments
- 利用onAttach方法
- Fragment向Activity傳值
- Fragment出棧問題
-
- Fragment退回棧
- 出棧問題
- Fragment重疊問題
- 下集預告
長話短說,省去一大堆套近乎的話,咱們先來聊聊Fragment的生面周期,一些學習從生面周期入手.
Fragment生命周期
Fragment的從生面周期流程如下:onAttach()->onCreate()->onCreateView()->onActivityCreate()->onStart()->onResume()->onPause()->onStop()->onDestroyView()->onDestroy()->onDetach(),咱們着重講幾個方法,其他的大部分同Activity的生命周期一個道理.
重要方法
方法 | 含義 | 注意點 |
---|---|---|
onAttach() | - | 我們可以通過該方法去擷取Fragment依附的Activity的執行個體 |
onCreate() | 初始化Fragment | - |
onCreateView() | 表示初始化Fragment的布局 | - |
onActivityCreate() | 表示Activity的onCreate執行完的回調 | 可進行UI的操作 |
onDestroyView() | 表示Fragment的視圖銷毀 | 但是Fragment任與Activity相綁定,可以通過調用onCreateView再次去初始化布局(ViewPager就是這樣) |
onDestroy() | 銷毀Fragment | - |
onDetach() | 解綁Activity | - |
學以緻用
通過基本的生面周期,我們就可以在自己的BaseFragment中運用到.
-
onAttach
onAttach方法在api23之前和之後所傳回的對象不一緻,是以我們可以擷取綁定的Activity的context對象.
public Context fragmentContext;
@TargetApi(23)
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
onAttachToContext(activity);
}
@SuppressWarnings("deprecation")
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
onAttachToContext(context);
}
public void onAttachToContext(Context context) {
this.fragmentContext = context;
}
-
onCreateView
該方法會去初始化布局,是以我們可以在該方法去綁定ButterKnife
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(getLayoutId(), container, false);
unbinder = ButterKnife.bind(this, rootView);
return rootView;
}
-
onDestory
那麼對應的onDestroy方法就可以去解綁ButterKnife
@Override
public void onDestroy() {
super.onDestroy();
if (unbinder != null)
unbinder.unbind();
}
Fragment懶加載
不論你的Fragment是搭配ViewPager還是Activity使用,Fragment可能會遇到Fragment預先加載的問題,就會導緻資源的使用過多,會出現卡頓延時的問題,如果你的Fragment的内容很複雜那麼影響就會更大,是以就需要利用到Fragment懶加載.
懶加載核心
可以通過getUserVisibleHint()和setUserVisibleHint()方法去實作懶加載.
方法 | 含義 | 注意點 |
---|---|---|
setUserVisibleHint(boolean isVisibleToUser) | isVisibleToUser表示目前Fragment是否可見 | 由于setUserVisibleHint回調的時間比onCreateView和onAttach還要早,故不能在該方法裡去操作UI,會報空 |
getUserVisibleHint() | 則是傳回目前Fragment是否可見的 | - |
以下非常重要
不過不是所有的Fragment都會回調setUserVisibleHint,隻有在FragmentAdapter中才會回調,通過hide/show/add去操作Fragment是不會有回調。
學以緻用
咱們去封裝一個懶加載的Fragment基類。下面的重要參數解釋
-
isViewInitiated
表示View是否初始化完.因為setUserVisibleHint的回調是最早的.
-
isVisibleToUser
表示setUserVisibleHint的回調的值,表示目前View是否可見.
-
isDataInitiated
表示資料是否第一次加載.
大家也可以自行調整,代碼如下
public abstract class BaseLazyFragment extends Fragment {
ProgressDialog progressDialog;
Unbinder unbinder;
protected boolean isViewInitiated;
protected boolean isVisibleToUser;onCreate()->onStart()->onResume()
protected boolean isDataInitiated;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(getLayoutId(), container, false);
unbinder = ButterKnife.bind(this, rootView);
return rootView;
}
@Override
public void onDestroy() {
super.onDestroy();
if (unbinder != null)
unbinder.unbind();
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
configViews();
}
protected abstract void configViews();
protected abstract int getLayoutId();
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
isViewInitiated = true;
prepareFetchData();
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
this.isVisibleToUser = isVisibleToUser;
prepareFetchData();
}
public abstract void fetchData();
public boolean prepareFetchData() {
return prepareFetchData(false);
}
public boolean prepareFetchData(boolean forceUpdate) {
if (isVisibleToUser && isViewInitiated && (!isDataInitiated || forceUpdate)) {
fetchData();
isDataInitiated = true;
return true;
}
return false;
}
}
Fragment與Activity互動
Fragment切換
這裡主要講FragmentManager操作Fragment.主要方式有兩種
1.通過add show hide去切換Fragment,那麼當Fragment第一次加載時,回去回調onCreate()->onStart()->onResume()方法.若是第二次,則onCreate()->onStart()->onResume()則不會回調.
2.通過replace切換,Fragment會走所有生面周期包括銷毀,消耗記憶體.
是以推薦通過show hide的方式去操作Fragment
Fragment傳值
Activity向Fragment傳值
setArguments
通過給Fragment的setArguments方法去設定所需要傳遞資料,如下
Fragment fragment2 = new Fragment_two();//fragment2對象
Bundle bundle = new Bundle();
bundle.putString("key",src);
fragment2.setArguments(bundle);//通過fragment2對象導入要傳的值
FragmentManager fragmentManager = getFragmentManager();//manger
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();//開啟一個事務
fragmentTransaction.replace(R.id.frag_2,fragment2);//加載fragment2
fragmentTransaction.commit();//送出事務
再在Fragment中調用getArguments()方式,去擷取傳遞進來的Bundle對象.
利用onAttach方法
可以通過onAttach方法,去擷取寄主Activity的context對象,去調用Activity的方法
首先在Activity中定義一個方法
public String getMsg() {
return "111";
}
其次在Fragment的onAttach方法中去擷取寄主Activity,去類型轉換進而調用
MainActivity _activity;
@TargetApi(23)
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
_activity = (MainActivity) activity;
_activity.getMsg();
}
Fragment向Activity傳值
其實方法有很多,可以通過Eventbus Rxbus,都可以,不過我所介紹的是通過接口回調的形式.
-
首先讓寄主Activity去實作一個回調接口
回調XxxListener接口,實作getMsg方法.那麼msg就表示Fragment所傳來的資料
public class MainActivity extends AppCompatActivity implements XxxListener {
@Override
public void getMsg(String msg) {
}
-
通過onAttach,轉換成XxxListener去回調對應的方法.
通過onAttach傳來的activity,去強制成XxxListener對象,進而去調用getMsg方法,想Activity傳遞資料
XxxListener xxxListener;
@TargetApi(23)
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
xxxListener = (XxxListener) activity;
xxxListener.getMsg("1111");
}
Fragment出棧問題
Fragment退回棧
一般情況下,Activity中使用了Fragment,哪怕存在很多個Fragment,這麼多個Fragment和Activity也是處于一個棧中,最直覺的現象就是,按一下back鍵整個Fragment和Activity一起退出;若想要Fragment像Activity一樣能添加到一個棧中,也就是按一下back,目前Fragment退出顯示上一個Fragment的這種效果,那麼我們就需要用到退回棧概念.
很簡單,就是在控制Fragment的時候,調用fragmentTransaction的addToBackStack(String name)方法,該方法表示把Fragment添加棧的隊列中,name就是一個辨別,為了便于了解,咱們看下圖
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHLuVzVZFDaXRmdsNjYoRmMMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL1cTMzIDM1UTM4EDOwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
出棧問題
若AFragment跳轉BFragment跳轉CFragment,那麼三個Fragment都添加到棧中,若此時我想要回退到AFragment要怎麼做?
可以通過一下兩個方法去出棧,回退到指定的Fragment
- popBackStack(String tag/int tag,int flag)
- popBackStackImmediate(String tag/int tag,int flag)
先來說說參數, String tag/int tag,表示add Fragment時候的辨別,那麼第二個參數表示是否包括自己,0表示不包括自己,1表示包括自己.
那麼兩個方法的差別在于什麼呢?
在于popBackStack方法不是馬上執行,而是把該操作添加到操作隊列中.popBackStackImmediate方法則是調用立即執行,立即出棧.
Fragment重疊問題
關于Fragment還會有一個問題,就是重疊問題.
那麼重疊問題是怎麼來的?咱們先了解記憶體重新開機的概念:每一個系統都給APP配置設定固定的記憶體,加上APP可能會有記憶體洩露導緻記憶體回收不及時,那麼就會被系統回收,重新開機APP.
FragmentManager管理這Fragment,因為記憶體重新開機,會從棧頂去恢複所有的Fragment,并且預設會以show的方式去顯示,就會出現重疊現象.
-
解決方法1:
通過Activity的onCreate的saveINstanceState去判斷是否記憶體重新開機,是的話再通過Manager的findFragmentByTag去找到Fragment,去根據需求去show hide指定的Fragment.
-
解決方法2:
寫一個BaseFragment的基類,在onSaveInstanceState去儲存是否隐藏,再從onCreate中去擷取記憶體重新開機錢的狀态去show hide
那麼Fragment那些事兒就結束了.
下集預告
Fragment那些事兒就告辭一段樓,下一篇會寫service&廣播那些事兒。主要内容有
- startService&bindService
- IntentService
- Aidl
- 廣播&适配