真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Android中怎么利用Fragment實(shí)現(xiàn)懶加載

這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)碛嘘P(guān)Android 中怎么利用Fragment實(shí)現(xiàn)懶加載,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

創(chuàng)新互聯(lián)堅(jiān)持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:成都網(wǎng)站制作、做網(wǎng)站、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶于互聯(lián)網(wǎng)時(shí)代的樂亭網(wǎng)站設(shè)計(jì)、移動(dòng)媒體設(shè)計(jì)的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!

多層嵌套后的 Fragment 懶加載

印象中從 Feed 流應(yīng)用流行開始,Fragment 懶加載變成了一個(gè)大家都需要關(guān)注的開發(fā)知識(shí),關(guān)于 Fragment 的懶加載,網(wǎng)上有很多例子,GitHub 上也有很多例子,就連我自己在一年前也寫過相關(guān)的文章。但是之前的應(yīng)用可能最多的是一層 Activity + ViewPager 的 UI 層次,但是隨著頁(yè)面越來越復(fù)雜,越來越多的應(yīng)用首頁(yè)一個(gè)頁(yè)面外層是一個(gè) ViewPager 內(nèi)部可能還嵌套著一層 ViewPager,這是之前的懶加載就可能不那么好用了。本文對(duì)于多層 ViewPager 的嵌套使用過程中,Fragment 主要的三個(gè)狀態(tài):第一次可見,每次可見,每次不可見,提供解決方案。

為什么要使用懶加載

在我們開發(fā)中經(jīng)常會(huì)使用 ViewPager + Fragment 來創(chuàng)建多 tab 的頁(yè)面,此時(shí)在 ViewPager 內(nèi)部默認(rèn)會(huì)幫我們緩存當(dāng)頁(yè)面前后兩個(gè)頁(yè)面的 Fragment 內(nèi)容,如果使用了 setOffscreenPageLimit 方法,那么 ViewPager 初始化的時(shí)候?qū)?huì)緩存對(duì)應(yīng)參數(shù)個(gè) Fragment。為了增加用戶體驗(yàn)我們往往會(huì)使用該方法來保證加載過的頁(yè)面不被銷毀,并留離開 tab 之前的狀態(tài)(列表滑動(dòng)距離等),而我們?cè)谑褂?Fragment 的時(shí)候往往在創(chuàng)建完 View 后,就會(huì)開始網(wǎng)絡(luò)請(qǐng)求等操作,如果存在上述的需求時(shí),懶加載就顯得尤為重要了,不僅可以節(jié)省用戶流量,還可以在提高應(yīng)用性能,給用戶帶來更加的體驗(yàn)。

ViewPager + Fragment 的懶加載實(shí)質(zhì)上我們就在做三件事,就可以將上邊所說的效果實(shí)現(xiàn),那么就是找到每個(gè) Fragment 第一對(duì)用戶可見的時(shí)機(jī),和每次 Fragment 對(duì)用戶可見時(shí)機(jī),以及每次 Framgment 對(duì)用戶不可見的時(shí)機(jī),來暴露給實(shí)現(xiàn)實(shí)現(xiàn)類做對(duì)應(yīng)的網(wǎng)絡(luò)請(qǐng)求或者網(wǎng)絡(luò)請(qǐng)求中斷時(shí)機(jī)。下面我們就來從常見的幾種 UI 結(jié)構(gòu)上一步步實(shí)現(xiàn)無論嵌套多少層,無論開發(fā)者使用的 hide show 還是 ViewPager 嵌套都能準(zhǔn)確獲取這三種狀態(tài)的時(shí)機(jī)的一種懶加載實(shí)現(xiàn)方案。

單層 ViewPager + Fragment 懶加載

我們都知道 Fragment 生命周期按先后順序有

onAttach -> onCreate -> onCreatedView -> onActivityCreated -> onStart -> onResume -> onPause -> onStop -> onDestroyView -> onDestroy -> onDetach

對(duì)于 ViewPager + Fragment 的實(shí)現(xiàn)我們需要關(guān)注的幾個(gè)生命周期有:

onCreatedView + onActivityCreated + onResume + onPause + onDestroyView

以及非生命周期函數(shù):

setUserVisibleHint + onHiddenChanged

對(duì)于單層 ViewPager + Fragment 可能是我們最常用的頁(yè)面結(jié)構(gòu)了,如網(wǎng)易云音樂的首頁(yè)頂部的是三個(gè) tab ,我們那網(wǎng)易云音樂作為例子:

Android 中怎么利用Fragment實(shí)現(xiàn)懶加載

對(duì)于這種 ViewPager + Fragment 結(jié)構(gòu),我們使用的過程中一般只包含是 4 種情況分別是:

1、使用 FragmentPagerAdapter ,FragmentPagerStateAdapter不設(shè)置 setOffscreenPageLimit

  1. 左右滑動(dòng)頁(yè)面,每次只緩存下一個(gè) Pager ,和上一個(gè) Pager

  2. 間隔的點(diǎn)擊 tab 如從位于 tab1 的時(shí)候直接選擇 tab3 或 tab4

2、使用 FragmentPagerAdapter,FragmentPagerStateAdapter 設(shè)置 setOffscreenPageLimit 為 tab 總數(shù)

  1. 左右滑動(dòng)頁(yè)面,每次只緩存下一個(gè) Pager ,和上一個(gè) Pager

  2. 間隔的點(diǎn)擊 tab 如從位于 tab1 的時(shí)候直接選擇 tab3 或 tab4

3、進(jìn)入其他頁(yè)面或者用戶按 home 鍵回到桌面,當(dāng)前 ViewPager 頁(yè)面變成不見狀態(tài)。

對(duì)于 FragmentPagerAdapterFragmentPagerStateAdapter 的區(qū)別在于在于,前者在 Fragment 不見的時(shí)候?qū)⒉粫?huì) detach ,而后者將會(huì)銷毀 Fragmentdetach 掉。

實(shí)際上這也是所有 ViewPager 的操作情況。

第一種情況不設(shè)置 setOffscreenPageLimit 左右滑動(dòng)頁(yè)面/或者每次選擇相鄰 tab 的情況 FragmentPagerAdapterFragmentPagerStateAdapter 有所區(qū)別

 BottomTabFragment1 setUserVisibleHint false
 BottomTabFragment2 setUserVisibleHint false
 BottomTabFragment1 setUserVisibleHint true

 BottomTabFragment1 onCreateView
 BottomTabFragment1 onActivityCreated 
 BottomTabFragment1 onResume 

 BottomTabFragment2 onCreateView
 BottomTabFragment2 onActivityCreated 
 BottomTabFragment2 onResume 

 //滑動(dòng)到 Tab 2
 BottomTabFragment3 setUserVisibleHint false
 BottomTabFragment1 setUserVisibleHint false
 BottomTabFragment2 setUserVisibleHint true

 BottomTabFragment3 onCreateView
 BottomTabFragment3 onActivityCreated 
 BottomTabFragment3 onResume 

 //跳過 Tab3 直接選擇 Tab4
 BottomTabFragment4 setUserVisibleHint false
 BottomTabFragment2 setUserVisibleHint false
 BottomTabFragment4 setUserVisibleHint true

 BottomTabFragment4 onCreateView
 BottomTabFragment4 onActivityCreated 
 BottomTabFragment4 onResume 

 BottomTabFragment2 onPause 
 BottomTabFragment2 onDestroyView

 // FragmentPagerStateAdapter 會(huì)走一下兩個(gè)生命周期方法
 BottomTabFragment2 onDestroy 
 BottomTabFragment2 onDetach 

 BottomTabFragment1 onPause 
 BottomTabFragment1 onDestroyView

 // FragmentPagerStateAdapter 會(huì)走一下兩個(gè)生命周期方法
 BottomTabFragment1 onDestroy 
 BottomTabFragment1 onDetach 

 // 用戶回到桌面 再回到當(dāng)前 APP 打開其他頁(yè)面當(dāng)前頁(yè)面的生命周期也是這樣的
 BottomTabFragment3 onPause 
 BottomTabFragment4 onPause 
 BottomTabFragment3 onStop 
 BottomTabFragment4 onStop 

 BottomTabFragment3 onResume 
 BottomTabFragment4 onResume

第二種情況設(shè)置 setOffscreenPageLimit 為 Pager 的個(gè)數(shù)時(shí)候,左右滑動(dòng)頁(yè)面/或者每次選擇相鄰 tab 的情況 FragmentPagerAdapter 和 FragmentPagerStateAdapter 沒有區(qū)別

 BottomTabFragment1 setUserVisibleHint false
 BottomTabFragment2 setUserVisibleHint false
 BottomTabFragment3 setUserVisibleHint false
 BottomTabFragment4 setUserVisibleHint false

 BottomTabFragment1 setUserVisibleHint true
 BottomTabFragment1 onCreateView
 BottomTabFragment1 onActivityCreated 
 BottomTabFragment1 onResume 

 BottomTabFragment2 onCreateView
 BottomTabFragment2 onActivityCreated 

 BottomTabFragment3 onCreateView
 BottomTabFragment3 onActivityCreated 

 BottomTabFragment4 onCreateView
 BottomTabFragment4 onActivityCreated 

 BottomTabFragment2 onResume 
 BottomTabFragment3 onResume 
 BottomTabFragment4 onResume 

 //選擇 Tab2
 BottomTabFragment1 setUserVisibleHint false
 BottomTabFragment2 setUserVisibleHint true

 //跳過 Tab3 直接選擇 Tab4
 BottomTabFragment2 setUserVisibleHint false
 BottomTabFragment4 setUserVisibleHint true

 // 用戶回到桌面 再回到當(dāng)前 APP 打開其他頁(yè)面當(dāng)前頁(yè)面的生命周期也是這樣的
 BottomTabFragment1 onPause 
 BottomTabFragment2 onPause 
 BottomTabFragment3 onPause 
 BottomTabFragment4 onPause 

 BottomTabFragment1 onResume 
 BottomTabFragment2 onResume 
 BottomTabFragment3 onResume 
 BottomTabFragment4 onResume

可以看出第一次執(zhí)行 setUserVisibleHint(boolean isVisibleToUser) 除了可見的 Fragment 外都為 false,還可以看出除了這一點(diǎn)不同以外,所有的 Fragment 都走到了生命周期 onResume 階段。而選擇相鄰 tab 的時(shí)候已經(jīng)初始化完成的Fragment 并不再重新走生命周期方法,只是 setUserVisibleHint(boolean isVisibleToUser) 為 true。當(dāng)用戶進(jìn)入其他頁(yè)面的時(shí)候所有 ViewPager 緩存的 Fragment 都會(huì)調(diào)用 onPause 生命周期函數(shù),當(dāng)再次回到當(dāng)前頁(yè)面的時(shí)候都會(huì)調(diào)用 onResume。

能發(fā)現(xiàn)這一點(diǎn),其實(shí)對(duì)于單層 ViewPager 嵌套 Fragment 可見狀態(tài)的把握其實(shí)已經(jīng)很明顯了。下面給出我的解決方案:

  1. 對(duì)于 Fragment 可見狀態(tài)的判斷需要設(shè)置兩個(gè)標(biāo)志位 ,F(xiàn)ragment View 創(chuàng)建完成的標(biāo)志位 isViewCreatedFragment 第一次創(chuàng)建的標(biāo)志位 mIsFirstVisible

  2. 為了獲得 Fragment 不可見的狀態(tài),和再次回到可見狀態(tài)的判斷,我們還需要增加一個(gè) currentVisibleState 標(biāo)志位,該標(biāo)志位在 onResume 中和 onPause 中結(jié)合 getUserVisibleHint 的返回值來決定是否應(yīng)該回調(diào)可見和不可見狀態(tài)函數(shù)。

整個(gè)可見過程判斷邏輯如下圖所示

Android 中怎么利用Fragment實(shí)現(xiàn)懶加載

Android 中怎么利用Fragment實(shí)現(xiàn)懶加載

接下來我們就來看下具體實(shí)現(xiàn):

public abstract class LazyLoadBaseFragment extends BaseLifeCircleFragment {

 protected View rootView = null;


 private boolean mIsFirstVisible = true;

 private boolean isViewCreated = false;

 private boolean currentVisibleState = false;


 @Override
 public void setUserVisibleHint(boolean isVisibleToUser) {
  super.setUserVisibleHint(isVisibleToUser);
  //走這里分發(fā)可見狀態(tài)情況有兩種,1. 已緩存的 Fragment 被展示的時(shí)候 2. 當(dāng)前 Fragment 由可見變成不可見的狀態(tài)時(shí)
  // 對(duì)于默認(rèn) tab 和 間隔 checked tab 需要等到 isViewCreated = true 后才可以通過此通知用戶可見,
  // 這種情況下第一次可見不是在這里通知 因?yàn)?nbsp;isViewCreated = false 成立,可見狀態(tài)在 onActivityCreated 中分發(fā)
  // 對(duì)于非默認(rèn) tab,View 創(chuàng)建完成 isViewCreated = true 成立,走這里分發(fā)可見狀態(tài),mIsFirstVisible 此時(shí)還為 false 所以第一次可見狀態(tài)也將通過這里分發(fā)
  if (isViewCreated){
   if (isVisibleToUser && !currentVisibleState) {
    dispatchUserVisibleHint(true);
   }else if (!isVisibleToUser && currentVisibleState){
    dispatchUserVisibleHint(false);
   }
  }
 }

 @Override
 public void onActivityCreated(@Nullable Bundle savedInstanceState) {
  super.onActivityCreated(savedInstanceState);

  // 將 View 創(chuàng)建完成標(biāo)志位設(shè)為 true
  isViewCreated = true;
  // 默認(rèn) Tab getUserVisibleHint() = true !isHidden() = true
  // 對(duì)于非默認(rèn) tab 或者非默認(rèn)顯示的 Fragment 在該生命周期中只做了 isViewCreated 標(biāo)志位設(shè)置 可見狀態(tài)將不會(huì)在這里分發(fā)
  if (!isHidden() && getUserVisibleHint()){
   dispatchUserVisibleHint(true);
  }

 }


 /**
  * 統(tǒng)一處理 顯示隱藏 做兩件事
  * 設(shè)置當(dāng)前 Fragment 可見狀態(tài) 負(fù)責(zé)在對(duì)應(yīng)的狀態(tài)調(diào)用第一次可見和可見狀態(tài),不可見狀態(tài)函數(shù) 
  * @param visible
  */
 private void dispatchUserVisibleHint(boolean visible) {

  currentVisibleState = visible;

  if (visible) {
   if (mIsFirstVisible) {
    mIsFirstVisible = false;
    onFragmentFirstVisible();
   }
   onFragmentResume();
  }else {
   onFragmentPause();
  }
 }

 /**
  * 該方法與 setUserVisibleHint 對(duì)應(yīng),調(diào)用時(shí)機(jī)是 show,hide 控制 Fragment 隱藏的時(shí)候,
  * 注意的是,只有當(dāng) Fragment 被創(chuàng)建后再次隱藏顯示的時(shí)候才會(huì)調(diào)用,第一次 show 的時(shí)候是不會(huì)回調(diào)的。
  */
 @Override
 public void onHiddenChanged(boolean hidden) {
  super.onHiddenChanged(hidden);
  if (hidden){
   dispatchUserVisibleHint(false);
  }else {
   dispatchUserVisibleHint(true);
  }
 }

 /**
  * 需要再 onResume 中通知用戶可見狀態(tài)的情況是在當(dāng)前頁(yè)面再次可見的狀態(tài) !mIsFirstVisible 可以保證這一點(diǎn),
  * 而當(dāng)前頁(yè)面 Activity 可見時(shí)所有緩存的 Fragment 都會(huì)回調(diào) onResume 
  * 所以我們需要區(qū)分那個(gè)Fragment 位于可見狀態(tài) 
  * (!isHidden() && !currentVisibleState && getUserVisibleHint())可條件可以判定哪個(gè) Fragment 位于可見狀態(tài)
  */
 @Override
 public void onResume() {
  super.onResume();
  if (!mIsFirstVisible){
   if (!isHidden() && !currentVisibleState && getUserVisibleHint()){
    dispatchUserVisibleHint(true);
   }
  }
 }

 /** 
  * 當(dāng)用戶進(jìn)入其他界面的時(shí)候所有的緩存的 Fragment 都會(huì) onPause
  * 但是我們想要知道只是當(dāng)前可見的的 Fragment 不可見狀態(tài),
  * currentVisibleState && getUserVisibleHint() 能夠限定是當(dāng)前可見的 Fragment
  */
 @Override
 public void onPause() {
  super.onPause();

  if (currentVisibleState && getUserVisibleHint()){
   dispatchUserVisibleHint(false);
  }
 }


 @Override
 public void onDestroyView() {
  super.onDestroyView();
  //當(dāng) View 被銷毀的時(shí)候我們需要重新設(shè)置 isViewCreated mIsFirstVisible 的狀態(tài)
  isViewCreated = false;
  mIsFirstVisible = true;
 }

 /**
  * 對(duì)用戶第一次可見
  */
 public void onFragmentFirstVisible(){
  LogUtils.e(getClass().getSimpleName() + " ");
 }

 /**
  * 對(duì)用戶可見
  */
 public void onFragmentResume(){
  LogUtils.e(getClass().getSimpleName() + " 對(duì)用戶可見");
 }

 /**
  * 對(duì)用戶不可見
  */
 public void onFragmentPause(){
  LogUtils.e(getClass().getSimpleName() + " 對(duì)用戶不可見");
 }


 @Override
 public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
  super.onCreateView(inflater,container,savedInstanceState);

  if (rootView == null) {
   rootView = inflater.inflate(getLayoutRes(), container, false);
  }

  initView(rootView);

  return rootView;
 }

 /**
  * 返回布局 resId
  *
  * @return layoutId
  */
 protected abstract int getLayoutRes();


 /**
  * 初始化view
  *
  * @param rootView
  */
 protected abstract void initView(View rootView);
}

我們使之前的 Fragment 改為繼承 LazyLoadBaseFragment 打印 log 可以看出:

//默認(rèn)選中第一 Tab
 BottomTabFragment1 setUserVisibleHint false
 BottomTabFragment2 setUserVisibleHint false
 BottomTabFragment3 setUserVisibleHint false
 BottomTabFragment4 setUserVisibleHint false
 BottomTabFragment1 setUserVisibleHint true

 BottomTabFragment1 onCreateView
 BottomTabFragment1 onActivityCreated 
 BottomTabFragment1 對(duì)用戶第一次可見
 BottomTabFragment1 對(duì)用戶可見
 BottomTabFragment1 onResume 

 BottomTabFragment2 onCreateView
 BottomTabFragment2 onActivityCreated 

 BottomTabFragment3 onCreateView
 BottomTabFragment3 onActivityCreated 

 BottomTabFragment4 onCreateView
 BottomTabFragment4 onActivityCreated 

 BottomTabFragment2 onResume 
 BottomTabFragment3 onResume 
 BottomTabFragment4 onResume 

 //滑動(dòng)選中 Tab2
 BottomTabFragment1 setUserVisibleHint false
 BottomTabFragment1 對(duì)用戶不可見

 BottomTabFragment2 setUserVisibleHint true
 BottomTabFragment2 對(duì)用戶第一次可見
 BottomTabFragment2 對(duì)用戶可見

 //間隔選中 Tab4
 BottomTabFragment2 setUserVisibleHint false
 BottomTabFragment2 對(duì)用戶不可見

 BottomTabFragment4 setUserVisibleHint true
 BottomTabFragment4 對(duì)用戶第一次可見
 BottomTabFragment4 對(duì)用戶可見


 // 回退到桌面
 BottomTabFragment1 onPause 
 BottomTabFragment2 onPause 
 BottomTabFragment3 onPause 
 BottomTabFragment4 onPause 
 BottomTabFragment4 對(duì)用戶不可見

 BottomTabFragment1 onStop 
 BottomTabFragment2 onStop 
 BottomTabFragment3 onStop 
 BottomTabFragment4 onStop 

 // 再次進(jìn)入 APP
 BottomTabFragment1 onResume 
 BottomTabFragment2 onResume 
 BottomTabFragment3 onResume 
 BottomTabFragment4 onResume 
 BottomTabFragment4 對(duì)用戶可見

上述 log 只演示了如何 ViewPager 中的函數(shù)打印,由于 hide show 方法顯示隱藏的 Fragment 有人可能認(rèn)為不需要懶加載這個(gè)東西,如果說從創(chuàng)建來說的確是這樣的,但是如果說所有的 Fragment 已經(jīng) add 進(jìn) Activity 中,此時(shí) Activity 退到后臺(tái),所有的 Fragment 都會(huì)調(diào)用 onPause ,并且在其進(jìn)入前臺(tái)的前臺(tái)統(tǒng)一會(huì)回調(diào) onResume, 如果我們?cè)?Resume 中做了某些操作,那么不可見的 Fragment 也會(huì)執(zhí)行,勢(shì)必也是個(gè)浪費(fèi)。所以這里的懶加載吧 hide show 的展示方法也考慮進(jìn)去。

對(duì)于無嵌套的 ViewPager ,懶加載還是相對(duì)簡(jiǎn)單的。但是對(duì)于ViewPager 嵌套 ViewPager 的情況可能就出現(xiàn)一些我們意料不到的情況。

雙層 ViewPager 嵌套的懶加載實(shí)現(xiàn)

對(duì)于雙層 ViewPager 嵌套我們也拿網(wǎng)易云來舉例:

Android 中怎么利用Fragment實(shí)現(xiàn)懶加載

可以看出頂層的第二 tab 內(nèi)部又是一個(gè) ViewPager ,那么我們?cè)囍凑瘴覀冎暗姆桨复蛴∫幌律芷谶^程:

BottomTabFragment1 setUserVisibleHint false
 BottomTabFragment2 setUserVisibleHint false

 BottomTabFragment1 setUserVisibleHint true
 BottomTabFragment1 onCreateView
 BottomTabFragment1 onActivityCreated 
 BottomTabFragment1 對(duì)用戶第一次可見
 BottomTabFragment1 對(duì)用戶可見

 BottomTabFragment1 onResume 
 BottomTabFragment2 onCreateView
 BottomTabFragment2 onActivityCreated 
 BottomTabFragment2 onResume 

 Bottom2InnerFragment1 setUserVisibleHint false
 Bottom2InnerFragment2 setUserVisibleHint false
 Bottom2InnerFragment1 setUserVisibleHint true

 //注意這里 位于第二個(gè)Tab 中的 ViewPager 中的第一個(gè) Tab 也走了可見,而它本身并不可見
 Bottom2InnerFragment1 onCreateView
 Bottom2InnerFragment1 onActivityCreated 
 Bottom2InnerFragment1 對(duì)用戶第一次可見
 Bottom2InnerFragment1 對(duì)用戶可見
 Bottom2InnerFragment1 onResume 

 Bottom2InnerFragment2 onCreateView
 Bottom2InnerFragment2 onActivityCreated 
 Bottom2InnerFragment2 onResume

咦奇怪的事情發(fā)生了,對(duì)于外層 ViewPager 的第二個(gè) tab 默認(rèn)是不顯示的,為什么內(nèi)部 ViewPager 中的 Bottom2InnerFragment1 卻走了可見了狀態(tài)回調(diào)。是不是 onActivityCreated 中的寫法有問題,!isHidden() && getUserVisibleHint() getUserVisibleHint() 方法通過 log 打印發(fā)現(xiàn)在 Bottom2InnerFragment1 onActivityCreated 時(shí)候, Bottom2InnerFragment1 setUserVisibleHint true的確是 true。所以才會(huì)走到分發(fā)可見事件中。

我們?cè)倩仡^看下上述的生命周期的打印,可以發(fā)現(xiàn),事實(shí)上作為父 Fragment 的 BottomTabFragment2 并沒有分發(fā)可見事件,他通過 getUserVisibleHint() 得到的結(jié)果為 false,首先我想到的是能在負(fù)責(zé)分發(fā)事件的方法中判斷一下當(dāng)前父 fragment 是否可見,如果父 fragment 不可見我們就不進(jìn)行可見事件的分發(fā),我們?cè)囍薷?dispatchUserVisibleHint 如下面所示:

 private void dispatchUserVisibleHint(boolean visible) {
  //當(dāng)前 Fragment 是 child 時(shí)候 作為緩存 Fragment 的子 fragment getUserVisibleHint = true
  //但當(dāng)父 fragment 不可見所以 currentVisibleState = false 直接 return 掉
  if (visible && isParentInvisible()) return;

  currentVisibleState = visible;

  if (visible) {
   if (mIsFirstVisible) {
    mIsFirstVisible = false;
    onFragmentFirstVisible();
   }
   onFragmentResume();
  } else {
   onFragmentPause();
  }
 }

 /**
 * 用于分發(fā)可見時(shí)間的時(shí)候父獲取 fragment 是否隱藏
 * @return true fragment 不可見, false 父 fragment 可見
 */
 private boolean isParentInvisible() {
 LazyLoadBaseFragment fragment = (LazyLoadBaseFragment) getParentFragment();
 return fragment != null && !fragment.isSupportVisible();

 }

private boolean isSupportVisible() {
 return currentVisibleState;
}

通過日志打印我們發(fā)現(xiàn)這似乎起作用了:

 BottomTabFragment1 setUserVisibleHint false
 BottomTabFragment2 setUserVisibleHint false

 BottomTabFragment1 setUserVisibleHint true
 BottomTabFragment1 onCreateView
 BottomTabFragment1 onActivityCreated 
 BottomTabFragment1 對(duì)用戶第一次可見
 BottomTabFragment1 對(duì)用戶可見

 BottomTabFragment1 onResume 
 BottomTabFragment2 onCreateView
 BottomTabFragment2 onActivityCreated 
 BottomTabFragment2 onResume 

 Bottom2InnerFragment1 setUserVisibleHint false
 Bottom2InnerFragment2 setUserVisibleHint false
 Bottom2InnerFragment1 setUserVisibleHint true

 Bottom2InnerFragment1 onCreateView
 Bottom2InnerFragment1 onActivityCreated 
 Bottom2InnerFragment1 onResume 

 Bottom2InnerFragment2 onCreateView
 Bottom2InnerFragment2 onActivityCreated 
 Bottom2InnerFragment2 onResume 

 //滑動(dòng)到第二個(gè) tab
 BottomTabFragment3 setUserVisibleHint false
 BottomTabFragment1 setUserVisibleHint false
 BottomTabFragment1 對(duì)用戶不可見

 BottomTabFragment2 setUserVisibleHint true
 BottomTabFragment2 對(duì)用戶第一次可見
 BottomTabFragment2 對(duì)用戶可見

 BottomTabFragment3 onCreateView
 BottomTabFragment3 onActivityCreated 
 BottomTabFragment3 onResume

但是我們又發(fā)現(xiàn)了新的問題,當(dāng)我們滑動(dòng)到第二個(gè) tab 時(shí)候,無疑我們期望得到第二個(gè) tab 中內(nèi)層 ViewPager 第一個(gè) tab 中 fragment 狀態(tài)的可見狀態(tài),但是從上邊的 log 可以發(fā)現(xiàn)我們并沒有獲得其可見狀態(tài)的打印,問題出當(dāng)外層 ViewPager 初始化的時(shí)候我們已經(jīng)經(jīng)歷了 Bottom2InnerFragment1 的初始化,而我們?cè)?dispatchUserVisibleHint 做了攔截,導(dǎo)致其無法分發(fā)可見事件,當(dāng)其真正可見的時(shí)候卻發(fā)現(xiàn)事件函數(shù)并不會(huì)再次被調(diào)用了。

本著堅(jiān)信一切困難都是紙老虎的社會(huì)主義光榮理念,我404了一下,發(fā)現(xiàn)網(wǎng)上極少的嵌套 fragment 懶加載的文章中,大多都采用了,在父 Fragment 可見的時(shí)候,分發(fā)自己可見狀態(tài)的同時(shí),把自己的可見狀態(tài)通知子 Fragment,對(duì)于可見狀態(tài)的 生命周期調(diào)用順序,父 Fragment總是優(yōu)先于子 Fragment,所以我們?cè)?Fragment 分發(fā)事件的時(shí)候,可以在上述攔截子 Fragment 事件分發(fā)后,當(dāng)在父 Fragment 第一可見的時(shí)候,通知子 Fragment 你也可見了。所以我再次修改 dispatchUserVisibleHint,在父 Fragment 分發(fā)完成自己的可見事件后,讓子 Fragment 再次調(diào)用自己的可見事件分發(fā)方法,這次 isParentInvisible() 將會(huì)返回 false ,也就是可見狀態(tài)將會(huì)正確分發(fā)。

private void dispatchUserVisibleHint(boolean visible) {
 //當(dāng)前 Fragment 是 child 時(shí)候 作為緩存 Fragment 的子 fragment getUserVisibleHint = true
 //但當(dāng)父 fragment 不可見所以 currentVisibleState = false 直接 return 掉
 if (visible && isParentInvisible()) return;

 currentVisibleState = visible;

 if (visible) {
  if (mIsFirstVisible) {
   mIsFirstVisible = false;
   onFragmentFirstVisible();
  }
  onFragmentResume();
  //可見狀態(tài)的時(shí)候內(nèi)層 fragment 生命周期晚于外層 所以在 onFragmentResume 后分發(fā)
  dispatchChildVisibleState(true);
 } else {
  onFragmentPause();
  dispatchChildVisibleState(false);

 }
}

 private void dispatchChildVisibleState(boolean visible) {
  FragmentManager childFragmentManager = getChildFragmentManager();
  List fragments = childFragmentManager.getFragments();
  if (!fragments.isEmpty()) {
   for (Fragment child : fragments) {
    // 如果只有當(dāng)前 子 fragment getUserVisibleHint() = true 時(shí)候分發(fā)事件,并將 也就是我們上面說的 Bottom2InnerFragment1
    if (child instanceof LazyLoadBaseFragment && !child.isHidden() && child.getUserVisibleHint()) {
     ((LazyLoadBaseFragment) child).dispatchUserVisibleHint(visible);
    }
   }
  }
 }

dispatchChildVisibleState 方法通過 childFragmentManager 獲取當(dāng)前 Fragment 中所有的子 Fragment 并通過判斷 child.getUserVisibleHint() 的返回值,判斷是否應(yīng)該通知子 Fragment 不可見,同理在父 Fragment 真正可見的時(shí)候,我們也會(huì)通過該方法,通知child.getUserVisibleHint() = true 的子 Fragment 你可見。

我們?cè)俅未蛴】梢钥闯鼋?jīng)過這次調(diào)整內(nèi)層 Fragment 已經(jīng)可以準(zhǔn)確地拿到自己第一次可見狀態(tài)了。

 BottomTabFragment3 setUserVisibleHint false
 BottomTabFragment1 setUserVisibleHint false
 BottomTabFragment1 對(duì)用戶不可見

 BottomTabFragment2 setUserVisibleHint true
 BottomTabFragment2 對(duì)用戶第一次可見
 BottomTabFragment2 對(duì)用戶可見

 Bottom2InnerFragment1 對(duì)用戶第一次可見
 Bottom2InnerFragment1 對(duì)用戶可見

 BottomTabFragment3 onCreateView
 BottomTabFragment3 onActivityCreated 
 BottomTabFragment3 onResume

當(dāng)我以為紙老虎一進(jìn)被我大打敗的時(shí)候,我按了下 home 鍵看了條微信,然后發(fā)現(xiàn) log 打印如下:

 BottomTabFragment1 onPause 

 //Bottom2InnerFragment1 第一不可見回調(diào)
 Bottom2InnerFragment1 onPause 
 Bottom2InnerFragment1 對(duì)用戶不可見

 Bottom2InnerFragment2 onPause 
 BottomTabFragment2 onPause 

 BottomTabFragment2 對(duì)用戶不可見
 //Bottom2InnerFragment1 第二次不可見回調(diào)
 Bottom2InnerFragment1 對(duì)用戶不可見
 BottomTabFragment3 onPause 
 BottomTabFragment1 onStop 

 Bottom2InnerFragment1 onStop 
 Bottom2InnerFragment2 onStop 

 BottomTabFragment2 onStop 
 BottomTabFragment3 onStop

這又是啥情況? 為啥回調(diào)了兩次,我連微信都忘了回就開始回憶之前分發(fā)可見事件的代碼,可見的時(shí)候時(shí)候沒問題,為什么不可見會(huì)回調(diào)兩次?后來發(fā)現(xiàn)問題出現(xiàn)在事件分發(fā)的順序上。

通過日志打印我們也可以看出,對(duì)于可見狀態(tài)的生命周期調(diào)用順序,父 Fragment總是優(yōu)先于子 Fragment,而對(duì)于不可見事件,內(nèi)部的 Fragment 生命周期總是先于外層 Fragment。所以第一的時(shí)候 Bottom2InnerFragment1 調(diào)用自身的 dispatchUserVisibleHint 方法分發(fā)了不可見事件,作為父 Fragment 的BottomTabFragment2 分發(fā)不可見的時(shí)候,又會(huì)再次調(diào)用 dispatchChildVisibleState ,導(dǎo)致子 Fragment 再次調(diào)用自己的 dispatchUserVisibleHint 再次調(diào)用了一次 onFragmentPause();

解決辦法也很簡(jiǎn)單,還記得 currentVisibleState 這個(gè)變量么? 表示當(dāng)前 Fragment 的可見狀態(tài),如果當(dāng)前的 Fragment 要分發(fā)的狀態(tài)與 currentVisibleState 相同我們就沒有必要去做分發(fā)了。

我們知道子 Fragment 優(yōu)于父 Fragment回調(diào)本方法 currentVisibleState 置位 false,當(dāng)前不可見,我們可以當(dāng)父 dispatchChildVisibleState 的時(shí)候第二次回調(diào)本方法 visible = false 所以此處 visible 將直接返回。

private void dispatchUserVisibleHint(boolean visible) {

 if (visible && isParentInvisible()) 
  return;

 // 此處是對(duì)子 Fragment 不可見的限制,因?yàn)?nbsp;子 Fragment 先于父 Fragment回調(diào)本方法 currentVisibleState 置位 false
 // 當(dāng)父 dispatchChildVisibleState 的時(shí)候第二次回調(diào)本方法 visible = false 所以此處 visible 將直接返回
 if (currentVisibleState == visible) {
  return;
 }

 currentVisibleState = visible;

 if (visible) {
  if (mIsFirstVisible) {
   mIsFirstVisible = false;
   onFragmentFirstVisible();
  }
  onFragmentResume();
  //可見狀態(tài)的時(shí)候內(nèi)層 fragment 生命周期晚于外層 所以在 onFragmentResume 后分發(fā)
  dispatchChildVisibleState(true);
 } else {
  onFragmentPause();
  dispatchChildVisibleState(false);
 }
}

對(duì)于 Hide And show 方法顯示的 Fragment 驗(yàn)證這里講不在過多贅述,上文也說了,對(duì)這種 Fragment 展示方法,我們更需要關(guān)注的是 hide 的時(shí)候, onPause 和 onResume 再次隱藏顯示的的時(shí)候。改方法的驗(yàn)證可以通過下載 Demo 查看 log。Demo 地址

最終的實(shí)現(xiàn)方案

下面是完整 LazyLoadBaseFragment 實(shí)現(xiàn)方案:也可以直接戳此下載文件 LazyLoadBaseFragment.java

import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.List;

/**
 * @author wangshijia
 * @date 2018/2/2
 * Fragment 第一次可見狀態(tài)應(yīng)該在哪里通知用戶 在 onResume 以后?
 */
public abstract class LazyLoadBaseFragment extends BaseLifeCircleFragment {

 protected View rootView = null;


 private boolean mIsFirstVisible = true;

 private boolean isViewCreated = false;

 private boolean currentVisibleState = false;

 @Override
 public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
  super.onCreateView(inflater, container, savedInstanceState);

  if (rootView == null) {
   rootView = inflater.inflate(getLayoutRes(), container, false);
  }
  initView(rootView);
  return rootView;
 }


 @Override
 public void setUserVisibleHint(boolean isVisibleToUser) {
  super.setUserVisibleHint(isVisibleToUser);
  // 對(duì)于默認(rèn) tab 和 間隔 checked tab 需要等到 isViewCreated = true 后才可以通過此通知用戶可見
  // 這種情況下第一次可見不是在這里通知 因?yàn)?nbsp;isViewCreated = false 成立,等從別的界面回到這里后會(huì)使用 onFragmentResume 通知可見
  // 對(duì)于非默認(rèn) tab mIsFirstVisible = true 會(huì)一直保持到選擇則這個(gè) tab 的時(shí)候,因?yàn)樵?nbsp;onActivityCreated 會(huì)返回 false
  if (isViewCreated) {
   if (isVisibleToUser && !currentVisibleState) {
    dispatchUserVisibleHint(true);
   } else if (!isVisibleToUser && currentVisibleState) {
    dispatchUserVisibleHint(false);
   }
  }
 }

 @Override
 public void onActivityCreated(@Nullable Bundle savedInstanceState) {
  super.onActivityCreated(savedInstanceState);

  isViewCreated = true;
  // !isHidden() 默認(rèn)為 true 在調(diào)用 hide show 的時(shí)候可以使用
  if (!isHidden() && getUserVisibleHint()) {
   dispatchUserVisibleHint(true);
  }

 }

 @Override
 public void onHiddenChanged(boolean hidden) {
  super.onHiddenChanged(hidden);
  LogUtils.e(getClass().getSimpleName() + " onHiddenChanged dispatchChildVisibleState hidden " + hidden);

  if (hidden) {
   dispatchUserVisibleHint(false);
  } else {
   dispatchUserVisibleHint(true);
  }
 }

 @Override
 public void onResume() {
  super.onResume();
  if (!mIsFirstVisible) {
   if (!isHidden() && !currentVisibleState && getUserVisibleHint()) {
    dispatchUserVisibleHint(true);
   }
  }
 }

 @Override
 public void onPause() {
  super.onPause();
  // 當(dāng)前 Fragment 包含子 Fragment 的時(shí)候 dispatchUserVisibleHint 內(nèi)部本身就會(huì)通知子 Fragment 不可見
  // 子 fragment 走到這里的時(shí)候自身又會(huì)調(diào)用一遍 ?
  if (currentVisibleState && getUserVisibleHint()) {
   dispatchUserVisibleHint(false);
  }
 }


 /**
  * 統(tǒng)一處理 顯示隱藏
  *
  * @param visible
  */
 private void dispatchUserVisibleHint(boolean visible) {
  //當(dāng)前 Fragment 是 child 時(shí)候 作為緩存 Fragment 的子 fragment getUserVisibleHint = true
  //但當(dāng)父 fragment 不可見所以 currentVisibleState = false 直接 return 掉
  // 這里限制則可以限制多層嵌套的時(shí)候子 Fragment 的分發(fā)
  if (visible && isParentInvisible()) return;

  //此處是對(duì)子 Fragment 不可見的限制,因?yàn)?nbsp;子 Fragment 先于父 Fragment回調(diào)本方法 currentVisibleState 置位 false
  // 當(dāng)父 dispatchChildVisibleState 的時(shí)候第二次回調(diào)本方法 visible = false 所以此處 visible 將直接返回
  if (currentVisibleState == visible) {
   return;
  }

  currentVisibleState = visible;

  if (visible) {
   if (mIsFirstVisible) {
    mIsFirstVisible = false;
    onFragmentFirstVisible();
   }
   onFragmentResume();
   dispatchChildVisibleState(true);
  } else {
   dispatchChildVisibleState(false);
   onFragmentPause();
  }
 }

 /**
  * 用于分發(fā)可見時(shí)間的時(shí)候父獲取 fragment 是否隱藏
  *
  * @return true fragment 不可見, false 父 fragment 可見
  */
 private boolean isParentInvisible() {
  LazyLoadBaseFragment fragment = (LazyLoadBaseFragment) getParentFragment();
  return fragment != null && !fragment.isSupportVisible();

 }

 private boolean isSupportVisible() {
  return currentVisibleState;
 }

 /**
  * 當(dāng)前 Fragment 是 child 時(shí)候 作為緩存 Fragment 的子 fragment 的唯一或者嵌套 VP 的第一 fragment 時(shí) getUserVisibleHint = true
  * 但是由于父 Fragment 還進(jìn)入可見狀態(tài)所以自身也是不可見的, 這個(gè)方法可以存在是因?yàn)閼c幸的是 父 fragment 的生命周期回調(diào)總是先于子 Fragment
  * 所以在父 fragment 設(shè)置完成當(dāng)前不可見狀態(tài)后,需要通知子 Fragment 我不可見,你也不可見,
  * 

  * 因?yàn)?nbsp;dispatchUserVisibleHint 中判斷了 isParentInvisible 所以當(dāng) 子 fragment 走到了 onActivityCreated 的時(shí)候直接 return 掉了   * 

  * 當(dāng)真正的外部 Fragment 可見的時(shí)候,走 setVisibleHint (VP 中)或者 onActivityCreated (hide show) 的時(shí)候   * 從對(duì)應(yīng)的生命周期入口調(diào)用 dispatchChildVisibleState 通知子 Fragment 可見狀態(tài)   *   * @param visible   */  private void dispatchChildVisibleState(boolean visible) {   FragmentManager childFragmentManager = getChildFragmentManager();   List fragments = childFragmentManager.getFragments();   if (!fragments.isEmpty()) {    for (Fragment child : fragments) {     if (child instanceof LazyLoadBaseFragment && !child.isHidden() && child.getUserVisibleHint()) {      ((LazyLoadBaseFragment) child).dispatchUserVisibleHint(visible);     }    }   }  }  public void onFragmentFirstVisible() {   LogUtils.e(getClass().getSimpleName() + " 對(duì)用戶第一次可見");  }  public void onFragmentResume() {   LogUtils.e(getClass().getSimpleName() + " 對(duì)用戶可見");  }  public void onFragmentPause() {   LogUtils.e(getClass().getSimpleName() + " 對(duì)用戶不可見");  }  @Override  public void onDestroyView() {   super.onDestroyView();   isViewCreated = false;   mIsFirstVisible = true;  }  /**   * 返回布局 resId   *   * @return layoutId   */  protected abstract int getLayoutRes();  /**   * 初始化view   *   * @param rootView   */  protected abstract void initView(View rootView); }

上述就是小編為大家分享的Android 中怎么利用Fragment實(shí)現(xiàn)懶加載了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。


當(dāng)前名稱:Android中怎么利用Fragment實(shí)現(xiàn)懶加載
文章URL:http://weahome.cn/article/jiocgs.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部