最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android仿微信Viewpager-Fragment惰性加載(lazy-loading)

 更新時(shí)間:2017年08月17日 16:04:36   作者:TellH  
這篇文章主要為大家詳細(xì)介紹了Android仿微信Viewpager-Fragment惰性加載lazy-loading,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

前言

今天起床,拿起手機(jī)開機(jī)第一時(shí)間當(dāng)然是打開微信了,左右滑動(dòng)Viewpager,發(fā)現(xiàn)它使用了一種叫惰性加載,或者說懶加載(lazy-loading)的方式加載Viewpager中的Fragment。效果如圖:

微信LazyLoading

什么是lazy-loading呢?顧名思義就是在必要的時(shí)候才加載,否則不進(jìn)行View的繪制和數(shù)據(jù)的加載。原因是Viewpager一次只會(huì)顯示一個(gè)頁卡,那么剛開始的時(shí)候,只需加載第一張F(tuán)ragment頁卡,其他的不加載,當(dāng)用戶向右滑動(dòng)切換再進(jìn)行加載。因?yàn)槠渌鸉ragment對(duì)于用戶來說是不可見的,如果一開始就把全部Fragment一起加載,可能造成啟動(dòng)時(shí)卡頓的問題,更重要的是可能白白耗費(fèi)用戶的流量,因?yàn)橛脩艨赡懿⒉恍枰渌鸉ragment的信息。

今天Google了有關(guān)Fragment惰性加載的資料,并沒有找到介紹得清楚詳細(xì)的博文+demo。所以我找到了Github上的一個(gè)開源項(xiàng)目demo里有關(guān)惰性加載的代碼,學(xué)習(xí)了這個(gè)知識(shí)點(diǎn),并把它整理出來分享給大家。

你應(yīng)該知道

你應(yīng)該知道viewPager.setOffscreenPageLimit();方法。該方法設(shè)置ViewPager允許有多少張pages存在于屏幕外(不包括正在顯示的page),默認(rèn)值是1。在范圍之外的pages 的View會(huì)被銷毀,即onDestroyView()會(huì)被執(zhí)行。

Viewpager里面FragmentPagerAdapter、FragmentStatePagerAdapter的區(qū)別:

1.FragmentPagerAdapter會(huì)將每一個(gè)生成的Fragment都放到內(nèi)存中,即無論怎么滑動(dòng)切換ViewPager,都不會(huì)有一個(gè)Fragment的onDestroy方法被調(diào)用。但Fragment不在viewPager.setOffscreenPageLimit(3);保護(hù)的范圍內(nèi)會(huì)調(diào)用FragmentManager的detach()方法,相應(yīng)的Fragment的onDestroyView會(huì)執(zhí)行,但Fragment實(shí)例仍在!所以該類適用于需要展示的Fragment比較少的情況。

2.FragmentStateAdapter 有點(diǎn)類似于LIstview的RecyclerBin機(jī)制,當(dāng)Fragment不在viewPager.setOffscreenPageLimit(3);保護(hù)的范圍內(nèi),F(xiàn)ragment的就會(huì)被銷毀,onDestroy()、onDetach()方法會(huì)被執(zhí)行。適用于要展示Fragment數(shù)量比較多,F(xiàn)ragment的子View和數(shù)據(jù)量復(fù)雜的情況。
熟知Fragment的生命周期。

Fragment的生命周期

Fragment的生命周期

3.剛被new出來的Fragment并沒有開始它的生命周期,當(dāng)它被添加到FragmentManager時(shí)生命周期才開始。

4.我們通常是在onCreateView()中對(duì)Fragment完成視圖的構(gòu)建。若是要實(shí)現(xiàn)延遲加載,可以在調(diào)用onCreateView時(shí)獲得一個(gè)空container的引用。當(dāng)?shù)却脩羟袚Q到屏幕的時(shí)候,開始加載數(shù)據(jù)和視圖。

那么如何得知我們的Fragment何時(shí)被切換到屏幕呢?核心方法就是getUserVisibleHint()和在Fragment中重寫setUserVisibleHint(boolean isVisibleToUser){…}方法。

在官方文檔是這樣描述該方法的:

public void setUserVisibleHint (boolean isVisibleToUser)
Added in API level 15
Set a hint to the system about whether this fragment's UI is currently visible to the user. This hint defaults to true and is persistent across fragment instance state save and restore.
An app may set this to false to indicate that the fragment's UI is scrolled out of visibility or is otherwise not directly visible to the user. This may be used by the system to prioritize operations such as fragment lifecycle updates or loader ordering behavior.
Parameters
isVisibleToUser true if this fragment's UI is currently visible to the user (default), false if it is not.

該方法的作用是設(shè)置一個(gè)提示或者標(biāo)志,該標(biāo)志代表的是Fragment在當(dāng)前是否處于對(duì)用戶的可見狀態(tài)。注意這里的可見并不能與Activity或Fragment的onStart或者onResume混淆。因?yàn)镕ragment處于onResume狀態(tài)并不代表它對(duì)用戶是可見的!仍覺得很困惑?那我們一起來Log一下吧。

我們把生命周期回調(diào)方法加了Log語句。

  @Override
  public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    Log.d("TAG", "setUserVisibleHint() called with: " + "isVisibleToUser = [" + isVisibleToUser + "]");
  }

我們?cè)试S有4張頁卡的緩存,因?yàn)槲⑿攀怯?個(gè)tab的。這樣ViewPager來回切換就不會(huì)有頁卡被銷毀了。

viewPager.setOffscreenPageLimit(3);

啟動(dòng)ViewPager后的Log:

D/TAG: setUserVisibleHint() called with: isVisibleToUser = [false]
D/TAG: setUserVisibleHint() called with: isVisibleToUser = [false]
D/TAG: setUserVisibleHint() called with: isVisibleToUser = [false]
D/TAG: setUserVisibleHint() called with: isVisibleToUser = [false]
D/TAG: setUserVisibleHint() called with: isVisibleToUser = [true]
D/TAG: onCreateView() : getUserVisibleHint():true
D/TAG: onStart() : getUserVisibleHint():true
D/TAG: onResume() : getUserVisibleHint():true
D/TAG: onCreateView() : getUserVisibleHint():false
D/TAG: onCreateView() : getUserVisibleHint():false
D/TAG: onCreateView() : getUserVisibleHint():false
D/TAG: onStart() : getUserVisibleHint():false
D/TAG: onResume() : getUserVisibleHint():false
D/TAG: onStart() : getUserVisibleHint():false
D/TAG: onResume() : getUserVisibleHint():false
D/TAG: onStart() : getUserVisibleHint():false
D/TAG: onResume() : getUserVisibleHint():false

從Log中,可得知,setUserVisibleHint()比onCreateView()先調(diào)用,并且只有一個(gè)方法的isVisbleToUser==true。由此我們可以斷定,正在展示的fragment對(duì)應(yīng)的isVisibleToUser才為true。我們現(xiàn)在有4個(gè)page,onCreateView()、onStart()、onResume()分別共調(diào)用了4次,由此可知,盡管Fragment沒有被展示,ViewPager也會(huì)將它們構(gòu)建起來,會(huì)回調(diào)onStart、onResume。那么ViewPager初始化時(shí)構(gòu)建Fragment的個(gè)數(shù)與什么有關(guān)呢?這個(gè)主要跟使用的Adapter類型和setOffscreenPageLimit()有關(guān)。

接下來向右滑,切換到第二頁,Log如下:

D/TAG: setUserVisibleHint() called with: isVisibleToUser = [false]
D/TAG: setUserVisibleHint() called with: isVisibleToUser = [true]

這次只會(huì)調(diào)用兩次setUserVisibleHint(),將要?jiǎng)倓傦@示的Fragment的isVisibleToUser 設(shè)置為false,并把將要顯示的Fragment的isVisibleToUser 設(shè)置為true。

當(dāng)我退出程序,Log如下:

D/TAG: onPause() : getUserVisibleHint():true
D/TAG: onPause() : getUserVisibleHint():false
D/TAG: onPause() : getUserVisibleHint():false
D/TAG: onPause() : getUserVisibleHint():false
D/TAG: onStop() called: getUserVisibleHint():true
D/TAG: onStop() called: getUserVisibleHint():false
D/TAG: onStop() called: getUserVisibleHint():false
D/TAG: onStop() called: getUserVisibleHint():false
D/TAG: onDestroyView() : getUserVisibleHint():true
D/TAG: onDestroy() :
D/TAG: onDetach() :
D/TAG: onDestroyView() : getUserVisibleHint():false
D/TAG: onDestroy() :
D/TAG: onDetach() :
D/TAG: onDestroyView() : getUserVisibleHint():false
D/TAG: onDestroy() :
D/TAG: onDetach() :
D/TAG: onDestroyView() : getUserVisibleHint():false
D/TAG: onDestroy() :
D/TAG: onDetach() :

從這“死亡日志”中,我們發(fā)現(xiàn),getUserVisibleHint()貫穿著Fragment的凋亡生命線。
到此,對(duì)這個(gè)關(guān)鍵的方法,我們算是有了一個(gè)宏觀的認(rèn)識(shí)。

具體實(shí)現(xiàn)

那么具體應(yīng)該怎么實(shí)現(xiàn)呢?我們可以在自定義一個(gè)抽象類LazyFragment,重寫onCreateView()方法,只返回一個(gè)簡單的,甚至是空的(不是null)的ViewGroup作為Container,比如return new FrameLayout();當(dāng)然這個(gè)ViewGroup我們需要保存為成員變量。接下來重寫setUserVisibleHint(boolean isVisibleToUser)方法,如果該Fragment處于用戶可見狀態(tài),就會(huì)調(diào)用該方法,并傳過來的isVisibleToUser==true。所以根據(jù)這個(gè)hint做一個(gè)判斷,若等于true,立即加載原本要正常顯示的視圖和數(shù)據(jù)。當(dāng)然這個(gè)方法可以作為一個(gè)抽象方法交給子類去實(shí)現(xiàn)。具體的實(shí)現(xiàn)就是這樣!Talk is simple,show you the code!

LazyFragment:

省去了一些,回調(diào)方法,只給出了核心的幾個(gè)方法,完整的可以看文章末尾的項(xiàng)目源碼。注釋已經(jīng)寫得相對(duì)完善,如果有不明白的地方歡迎評(píng)論留言。

public class LazyFragment extends BaseFragment {
 private boolean isInit = false;//真正要顯示的View是否已經(jīng)被初始化(正常加載)
 private Bundle savedInstanceState;
 public static final String INTENT_BOOLEAN_LAZYLOAD = "intent_boolean_lazyLoad";
 private boolean isLazyLoad = true;
 private FrameLayout layout;
 private boolean isStart = false;//是否處于可見狀態(tài),in the screen

 @Deprecated
 protected final void onCreateView(Bundle savedInstanceState) {
  Log.d("TAG", "onCreateView() : " + "getUserVisibleHint():" + getUserVisibleHint());
  super.onCreateView(savedInstanceState);
  Bundle bundle = getArguments();
  if (bundle != null) {
   isLazyLoad = bundle.getBoolean(INTENT_BOOLEAN_LAZYLOAD, isLazyLoad);
  }
  //判斷是否懶加載
  if (isLazyLoad) {
   //處于完全可見、沒被初始化的狀態(tài),調(diào)用onCreateViewLazy顯示內(nèi)容
   if (getUserVisibleHint() && !isInit) {
    this.savedInstanceState = savedInstanceState;
    onCreateViewLazy(savedInstanceState);
    isInit = true;
   } else {
    //進(jìn)行懶加載
    layout = new FrameLayout(getApplicationContext());
    layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
    View view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.fragment_lazy_loading, null);
    layout.addView(view);
    super.setContentView(layout);
   }
  } else {
   //不需要懶加載,開門江山,調(diào)用onCreateViewLazy正常加載顯示內(nèi)容即可
   onCreateViewLazy(savedInstanceState);
   isInit = true;
  }
 }

 @Override
 public void setUserVisibleHint(boolean isVisibleToUser) {
  super.setUserVisibleHint(isVisibleToUser);
  Log.d("TAG", "setUserVisibleHint() called with: " + "isVisibleToUser = [" + isVisibleToUser + "]");
  //一旦isVisibleToUser==true即可對(duì)真正需要的顯示內(nèi)容進(jìn)行加載

  //可見,但還沒被初始化
  if (isVisibleToUser && !isInit && getContentView() != null) {
   onCreateViewLazy(savedInstanceState);
   isInit = true;
   onResumeLazy();
  }
  //已經(jīng)被初始化(正常加載)過了
  if (isInit && getContentView() != null) {
   if (isVisibleToUser) {
    isStart = true;
    onFragmentStartLazy();
   } else {
    isStart = false;
    onFragmentStopLazy();
   }
  }
 }

 @Override
 public void setContentView(int layoutResID) {
  //判斷若isLazyLoad==true,移除所有l(wèi)azy view,加載真正要顯示的view
  if (isLazyLoad && getContentView() != null && getContentView().getParent() != null) {
   layout.removeAllViews();
   View view = inflater.inflate(layoutResID, layout, false);
   layout.addView(view);
  }
  //否則,開門見山,直接加載
  else {
   super.setContentView(layoutResID);
  }
 }

 @Override
 public void setContentView(View view) {
  //判斷若isLazyLoad==true,移除所有l(wèi)azy view,加載真正要顯示的view
  if (isLazyLoad && getContentView() != null && getContentView().getParent() != null) {
   layout.removeAllViews();
   layout.addView(view);
  }
  //否則,開門見山,直接加載
  else {
   super.setContentView(view);
  }
 }
}

具體的實(shí)現(xiàn)類:

public class MoreFragment extends LazyFragment {
 private TextView tvLoading;
 private ImageView ivContent;
 private int tabIndex;
 public static final String INTENT_INT_INDEX="index";

 public static MoreFragment newInstance(int tabIndex) {

  Bundle args = new Bundle();
  args.putInt(INTENT_INT_INDEX, tabIndex);
  MoreFragment fragment = new MoreFragment();
  fragment.setArguments(args);
  return fragment;
 }
 @Override
 protected void onCreateViewLazy(Bundle savedInstanceState) {
  super.onCreateViewLazy(savedInstanceState);
  setContentView(R.layout.fragment_tabmain_item);
  tabIndex = getArguments().getInt(INTENT_INT_INDEX);
  ivContent = (ImageView) findViewById(R.id.iv_content);
  tvLoading = (TextView) findViewById(R.id.tv_loading);
  getData();
 }

 private void getData() {
  new Thread(new Runnable() {
   @Override
   public void run() {
    //異步處理加載數(shù)據(jù)
    //...
    //完成后,通知主線程更新UI
    handler.sendEmptyMessageDelayed(1, 2000);
   }
  }).start();
 }

 @Override
 public void onDestroyViewLazy() {
  super.onDestroyViewLazy();
  handler.removeMessages(1);
 }

 private Handler handler = new Handler() {
  public void handleMessage(android.os.Message msg) {
   tvLoading.setVisibility(View.GONE);
   int id=0;
   switch (tabIndex){
    case 1:
     id=R.drawable.a;
     break;
    case 2:
     id=R.drawable.b;
     break;
    case 3:
     id=R.drawable.c;
     break;
    case 4:
     id=R.drawable.d;
     break;
   }
   ivContent.setImageResource(id);
   ivContent.setVisibility(View.VISIBLE);
  }
 };
}


為了簡化布局,demo中只用了微信上的幾張截圖,希望大家能專注重點(diǎn)。具體效果如圖:

這里寫圖片描述

聽說留下完整示例代碼和demo是一種美德。(^__^) —Github跳轉(zhuǎn)

以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Android使用view仿支付寶月賬單

    Android使用view仿支付寶月賬單

    這篇文章主要為大家詳細(xì)介紹了Android使用view仿支付寶月賬單,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-04-04
  • Android存儲(chǔ)卡讀寫文件與Application數(shù)據(jù)保存的實(shí)現(xiàn)介紹

    Android存儲(chǔ)卡讀寫文件與Application數(shù)據(jù)保存的實(shí)現(xiàn)介紹

    這篇文章主要介紹了Android在存儲(chǔ)卡上讀寫文件、Application保存數(shù)據(jù)的實(shí)現(xiàn)步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2022-09-09
  • Android 安全加密:非對(duì)稱加密詳解

    Android 安全加密:非對(duì)稱加密詳解

    本文主要介紹Android 安全加密,非對(duì)稱加密的知識(shí),這里整理了詳細(xì)的資料,及使用方法,有需要的小伙伴可以參考下
    2016-09-09
  • 詳解Android中motion_toast的使用

    詳解Android中motion_toast的使用

    我們通常會(huì)用 toast(也叫吐司)來顯示提示信息,例如網(wǎng)絡(luò)請(qǐng)求錯(cuò)誤,校驗(yàn)錯(cuò)誤等等。本文為大家介紹一個(gè)非常有趣的toast組件 —— motion_toast,感興趣的可以了解一下
    2022-06-06
  • 詳解Android 通過Socket 和服務(wù)器通訊(附demo)

    詳解Android 通過Socket 和服務(wù)器通訊(附demo)

    Android 通過Socket 和服務(wù)器通訊,是一種比較常用的通訊方式,這篇文章主要介紹了詳解Android 通過Socket 和服務(wù)器通訊,有興趣的可以了解一下。
    2016-12-12
  • Android開發(fā)實(shí)現(xiàn)ScrollView中嵌套兩個(gè)ListView的方法

    Android開發(fā)實(shí)現(xiàn)ScrollView中嵌套兩個(gè)ListView的方法

    這篇文章主要介紹了Android開發(fā)實(shí)現(xiàn)ScrollView中嵌套兩個(gè)ListView的方法,結(jié)合實(shí)例形式分析了Android ScrollView中嵌套兩個(gè)ListView的操作技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2017-11-11
  • Android利用POI實(shí)現(xiàn)將圖片插入到Excel

    Android利用POI實(shí)現(xiàn)將圖片插入到Excel

    這篇文章主要為大家詳細(xì)介紹了Android如何利用POI實(shí)現(xiàn)將圖片插入到Excel,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-11-11
  • Android Fragment監(jiān)聽返回鍵的一種合理方式

    Android Fragment監(jiān)聽返回鍵的一種合理方式

    這篇文章主要給大家介紹了關(guān)于Android Fragment監(jiān)聽返回鍵的一種合理方式,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • 詳解Android Dialog對(duì)話框的五種形式

    詳解Android Dialog對(duì)話框的五種形式

    這篇文章主要為大家詳細(xì)介紹了Android對(duì)話框的五種形式,一般對(duì)話框,列表對(duì)話框,單選按鈕對(duì)話框,多選按鈕對(duì)話框,自定義對(duì)話框,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • Android fragment實(shí)現(xiàn)按鈕點(diǎn)擊事件的示例講解

    Android fragment實(shí)現(xiàn)按鈕點(diǎn)擊事件的示例講解

    下面小編就為大家分享一篇Android fragment實(shí)現(xiàn)按鈕點(diǎn)擊事件的示例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-01-01

最新評(píng)論

临海市| 曲麻莱县| 额尔古纳市| 江城| 呼玛县| 丹巴县| 乐都县| 阳信县| 凤城市| 都江堰市| 黄冈市| 聂拉木县| 徐汇区| 驻马店市| 霍邱县| 栖霞市| 阿拉善右旗| 榆树市| 平乐县| 文安县| 临夏县| 巴林右旗| 松潘县| 阿城市| 应城市| 无锡市| 安岳县| 伊川县| 莱州市| 平泉县| 蚌埠市| 金坛市| 阳朔县| 五莲县| 巴林左旗| 西宁市| 永吉县| 封开县| 宜君县| 桃园县| 新和县|