我們在做應用開發的時候,一個Activity里面可能會以viewpager(或其他容器)與多個Fragment來組合使用,而如果每個fragment都需要去加載數據,或從本地加載,或從網絡加載,那么在這個activity剛創建的時候就變成需要初始化大量資源。這樣的結果,我們當然不會滿意。那么,能不能做到當切換到這個fragment的時候,它才去初始化呢?
答案就在Fragment里的setUserVisibleHint這個方法里。請看關於Fragment里這個方法的API文檔:
該方法用於告訴系統,這個Fragment的UI是否是可見的。所以我們只需要繼承Fragment並重寫該方法,即可實現在fragment可見時才進行數據加載操作,即Fragment的懶加載。根據網友們提供的方法,代碼如下(本人稍作修改了下):
1 import android.os.Bundle; 2 import android.support.v4.app.Fragment; 3 4 /** 5 * Author: wangjie 6 * Email: tiantian.china.2@gmail.com 7 * Date: 1/23/15. 8 */ 9 public abstract class BaseLazyFragment extends Fragment { 10 private static final String TAG = BaseLazyFragment.class.getSimpleName(); 11 private boolean isPrepared; 12 13 @Override 14 public void onActivityCreated(Bundle savedInstanceState) { 15 super.onActivityCreated(savedInstanceState); 16 initPrepare(); 17 } 18 19 20 /** 21 * 第一次onResume中的調用onUserVisible避免操作與onFirstUserVisible操作重復 22 */ 23 private boolean isFirstResume = true; 24 25 @Override 26 public void onResume() { 27 super.onResume(); 28 if (isFirstResume) { 29 isFirstResume = false; 30 return; 31 } 32 if (getUserVisibleHint()) { 33 onUserVisible(); 34 } 35 } 36 37 @Override 38 public void onPause() { 39 super.onPause(); 40 if (getUserVisibleHint()) { 41 onUserInvisible(); 42 } 43 } 44 45 private boolean isFirstVisible = true; 46 private boolean isFirstInvisible = true; 47 48 @Override 49 public void setUserVisibleHint(boolean isVisibleToUser) { 50 super.setUserVisibleHint(isVisibleToUser); 51 if (isVisibleToUser) { 52 if (isFirstVisible) { 53 isFirstVisible = false; 54 initPrepare(); 55 } else { 56 onUserVisible(); 57 } 58 } else { 59 if (isFirstInvisible) { 60 isFirstInvisible = false; 61 onFirstUserInvisible(); 62 } else { 63 onUserInvisible(); 64 } 65 } 66 } 67 68 public synchronized void initPrepare() { 69 if (isPrepared) { 70 onFirstUserVisible(); 71 } else { 72 isPrepared = true; 73 } 74 } 75 76 /** 77 * 第一次fragment可見(進行初始化工作) 78 */ 79 public abstract void onFirstUserVisible(); 80 81 /** 82 * fragment可見(切換回來或者onResume) 83 */ 84 public abstract void onUserVisible(); 85 86 /** 87 * 第一次fragment不可見(不建議在此處理事件) 88 */ 89 public abstract void onFirstUserInvisible(); 90 91 /** 92 * fragment不可見(切換掉或者onPause) 93 */ 94 public abstract void onUserInvisible(); 95 96 }
如上代碼,使用setUserVisibleHint方法作為回調的依據,
暴露出來讓子類使用的新的生命周期方法為:
- onFirstUserVisible();
第一次fragment可見(進行初始化工作)
- onUserVisible();
fragment可見(切換回來或者onResume)
- onFirstUserInvisible();
第一次fragment不可見(不建議在此處理事件)
- onUserInvisible();
fragment不可見(切換掉或者onPause)
據說具體的效果是:
1. 首先加載ViewPager,回調FragmentA(第一個默認呈現的Fragment)的onFirstUserVisible(),可以在這里進行FragmentA的初始化工作,其他Fragment保持不變。
2. 用戶從FragmentA滑動到FragmentB,回調FragmentA的onUserInvisible()、FragmentB的onFirstUserVisible()(因為第一次切換到FragmentB,可以在這里進行初始化工作)。
3. 用戶從FragmentB滑動到FragmentC,回調FragmentB的onUserInvisible()、FragmentC的onFirstUserVisible()(因為第一次切換到FragmentC,可以在這里進行初始化工作)。
4. 用戶從FragmentC滑動到FragmentB,回調FragmentC的onUserInvisible()、FragmentB的onUserVisible()(因為FragmentB之前已經被加載過)。
5. 因為到此為止,suoyou的Fragment都已經被加載過了,所以以后這3個Fragment互相任意切換,只會回調原來Fragment的onUserInvisible()和切換后的Fragment的onUserVisible()。
6. 用戶處於FragmentB,關閉手機屏幕,回調FragmentB的onUserInvisible()。
7. 用戶處於FragmentB,手機屏幕處關閉狀態,然后開啟手機屏幕解鎖,只回調FragmentB的onUserVisible()。
可我TM無論怎么調試都沒這個效果好嗎,TMD setUserVisibleHint()就是不調用,不執行!!!
問度娘TMD都有是千篇一律的是使用fragment的setUserVisibleHint()方法實現懶加載,使用fragment的setuservisiblehint ()方法實現懶加載,使用fragment的setUserVisibleHint()方法實現懶加載;我去!!!我都快崩潰了好嗎。
無果只能求助國外的網友了,豎上梯子問google(搜索 fragment setuservisiblehint not called)我的男神去了,打開第一條搜索結果,這個結果是我的另一個男神:,這就是我正想要的,國外的網友給的答案如下:
答案大致意思是:需要 FragmentPagerAdapter 顯示的對setUserVisibleHint()方法的調用,查看自己的adapter原來是繼承的PagerAdapter 而不是FragmentPagerAdapter,於是果斷重新生成一個繼承 FragmentPagerAdapter 的 adapter,
代碼如下:
1 3 import android.support.v4.app.Fragment; 4 import android.support.v4.app.FragmentManager; 5 import android.support.v4.app.FragmentPagerAdapter; 6 7 import java.util.ArrayList; 8 import java.util.List; 9 10 public class SimpleFragmentPagerAdapter extends FragmentPagerAdapter { 11 private List<Fragment> listFragments; 12 private List<String> mTitleList = new ArrayList<>();//頁卡標題集合 13 14 public SimpleFragmentPagerAdapter(FragmentManager fm, 15 List<Fragment> al, 16 List<String> titleList) { 17 super(fm); 18 listFragments = al; 19 mTitleList = titleList; 20 } 21 22 public SimpleFragmentPagerAdapter(FragmentManager fm) { 23 super(fm); 24 } 25 26 @Override 27 public Fragment getItem(int position) { 28 return listFragments.get(position); 29 } 30 31 @Override 32 public int getCount() { 33 return listFragments.size(); 34 } 35 36 @Override 37 public int getItemPosition(Object object) { 38 return super.getItemPosition(object); 39 } 40 41 @Override 42 public CharSequence getPageTitle(int position) { 43 return mTitleList.get(position);//頁卡標題 44 } 45 }
然后將該adapter賦予Viewpager ,經調試成功了,setUserVisibleHint()方法終於起作用了,懶加載也有了。
項目源碼:https://github.com/Leevey/LazyLoadFragment
參考文獻:
實現類似微信的延遲加載的Fragment——LazyFragment
stackoverflow:Is Fragment.setUserVisibleHint() called by the android System?