天天看點

Fragment那些事兒Fragment生命周期Fragment懶加載Fragment與Activity互動Fragment出棧問題Fragment重疊問題下集預告

目錄

  • 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就是一個辨別,為了便于了解,咱們看下圖

Fragment那些事兒Fragment生命周期Fragment懶加載Fragment與Activity互動Fragment出棧問題Fragment重疊問題下集預告

出棧問題

若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
  • 廣播&适配

繼續閱讀