這個只是一個簡單的類,具體放在recyclerview-adapter-hf庫的目錄下园担,作為了demo的一部分奏纪,有需要的可以看一下腌乡。
需求
- ViewPager在切換時Fragment能正確回調(diào)可見性
- 頁面跳轉(zhuǎn)京髓、頁面前后臺切換時能正確回調(diào)可見性
- 有子集ViewPager嵌套Fragment時能正確回調(diào)可見性
- 根據(jù)Fragment 真實可見性進行view及數(shù)據(jù)初始化操作
場景限定
這里描述的場景定義在ViewPager中使用Fragment(大多數(shù)情況下Fragment在ViewPager中)以及單頁面Fragment
擴展及回調(diào)API
/**
* 視圖初始化回調(diào)
*
* @param inflater
* @param container
* @param savedInstanceState
* @return
*/
public abstract View onLazyCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState);
/**
* 適用與外層是viewpager(vp_outer) 航缀,viewpager中某個fragment中也用了viewpager(vp_inner)時,用來設(shè)置vp_inner
*
* @return
*/
protected ViewPager setNestedViewPagerWithNestedFragment() {
return null;
}
/**
* fragment可見性變化時回調(diào)
*
* Fragment 可見時該方法會回調(diào)一次, 不可見時保證最少調(diào)用一次
* @param visible
*/
protected void onFragmentVisibilityChanged(boolean visible) {}
/**
* 是否開啟view的懶加載模式
*
* @return
*/
protected boolean isViewLazyLoadEnable() {
return true;
}
功能實現(xiàn)
實現(xiàn)簡介
- ViewPager在切換時內(nèi)部會自動調(diào)用 setUserVisibleHint(...)處理Fragment可見性(也僅限于關(guān)聯(lián)到自身切換的Fragment)
- 非ViewPager切換引起的可見性變化需要用Fragment的生命周期方法
onStart()堰怨、onStop()
進行處理(比如:我們希望在頁面跳轉(zhuǎn)以及頁面前后臺切換時也能張確的進行可見性回調(diào)) - ViewPager嵌套時
實現(xiàn)
- 加入以下兩個方法作為懶加載模式的配置以及Fragment可見性回調(diào)
/**
* fragment可見性變化時回調(diào)
*
* @param visible
*/
protected void onFragmentVisibilityChanged(boolean visible) {}
/**
* 是否開啟view的懶加載模式
*
* @return
*/
protected boolean isViewLazyLoadEnable() {
return true;
}
- 懶加載初始化及通過ViewPager進行Fragment可見性回調(diào)處理
我們都知道Fragment中視圖的創(chuàng)建是在public View onCreateView(...)
方法中芥玉,那么對于視圖懶加載來說視圖的初始化時機需要延后處理,通過結(jié)合ViewPager內(nèi)部調(diào)用public void setUserVisibleHint(...)
在當切換到當前Fragment時在進行視圖初始化操作备图,這個過程需要注意以下幾點:
- 初始創(chuàng)建時
public void setUserVisibleHint(...)
優(yōu)先執(zhí)行于public View onCreateView(...)
- 系統(tǒng)只會回調(diào)
public View onCreateView(...)
一次灿巧,所以對于懶加載需要提前創(chuàng)建一個新的rootView作為根視圖占位public abstract View onLazyCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState);
為新的視圖初始化回調(diào)方法,通過實現(xiàn)進行其他初始化操作
該部分只是描述了懶加載初始化以及通過ViewPager的特點進行Fragment可見性的回調(diào)處理诬烹,代碼如下:
private Bundle mSavedInstanceState;
/** 標識客戶端是否真正初始化了視圖, 通過調(diào)用{@link #lazyCreateView} **/
private boolean mIsRealViewSetup;
private View mRootView;
/** 是否已經(jīng)調(diào)用了初始化view方法 **/
private boolean mIsCalledOnCreateViewMethod = false;
@Nullable
@Override
final
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (this.isViewLazyLoadEnable() == false || originVisibleOfUserHint) {//不是懶加載
Log.e("LazyLoadFragment", "onCreateView() -> will call lazyCreateView() ");
this.mRootView = lazyCreateView(LayoutInflater.from(getContext()), container, mSavedInstanceState);
this.mIsRealViewSetup = true;
} else {
Log.e("LazyLoadFragment", "onCreateView() -> init by FrameLayout ");
this.mRootView = new FrameLayout(getContext());
}
Log.e("LazyLoadFragment", "onCreateView -> " + isViewLazyLoadEnable() + " , >> " + getClass().getSimpleName());
this.mSavedInstanceState = savedInstanceState;
this.mIsCalledOnCreateViewMethod = true;
return mRootView;
}
private boolean originVisibleOfUserHint = false;
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
this.originVisibleOfUserHint = getUserVisibleHint();
Log.e("LazyLoadFragment", "setUserVisibleHint -> " + isVisibleToUser + " , originVisibleOfUserHint: " + originVisibleOfUserHint + " ]]> " + getClass().getSimpleName() + " , rootView: " + mRootView);
if (this.mRootView == null) {
return;
}
if (this.isViewLazyLoadEnable() && isVisibleToUser && mIsCalledOnCreateViewMethod == true && mIsRealViewSetup == false) {
Log.e("LazyLoadFragment", "setUserVisibleHint() -> will call lazyCreateView() ");
ViewGroup rootView = (ViewGroup) mRootView;
rootView.removeAllViews();
View contentView = lazyCreateView(LayoutInflater.from(getContext()), rootView, mSavedInstanceState);
rootView.addView(contentView);
this.mIsRealViewSetup = true;
}
if (this.mIsRealViewSetup) {
this.onFragmentVisibilityChanged(isVisibleToUser, false);
}
}
private View lazyCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.e("LazyLoadFragment", "lazyCreateView -> [-" + getClass().getSimpleName() + "-]");
return this.onLazyCreateView(inflater, container, savedInstanceState);
}
public abstract View onLazyCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState);
/** 記錄當前fragment可見狀態(tài) **/
public boolean mCurrentFragmentVisibility = false;
/**
* fragment可見性變化時回調(diào)
*
* @param isVisibleToUser 當前fragment是否前臺可見
* @param isLifeCycle 當前fragment可見性是否由fragment生命周期變化引起 false: 由調(diào)用{@link #setUserVisibleHint}引起
*/
@CallSuper
private void onFragmentVisibilityChanged(boolean isVisibleToUser, boolean isLifeCycle) {
this.mCurrentFragmentVisibility = isVisibleToUser;
Fragment fragment = getParentFragment();
boolean fragmentVisible = false;
if (fragment instanceof LazyLoadFragment) {
//處理view非懶加載時有二級fragment viewpager情況第一次初始化時的問題
//這種情況下一級fragment不可見砸烦,但二級viewpager中fragment初始化后會自動設(shè)置二級fragment的可見性
fragmentVisible = ((LazyLoadFragment) fragment).mCurrentFragmentVisibility;
if (!fragmentVisible && isLifeCycle) {
return;
}
}
this.onFragmentVisibilityChanged(isVisibleToUser);
}
- 通過Fragment生命周期方法處理可見性回調(diào)
在頁面跳轉(zhuǎn)以及Fragment進行了前后臺切換時弃鸦,我們?nèi)匀幌M苷_及時的知道Fragment的可見情況绞吁,比如頁面跳走或者切到后臺時Fragment應(yīng)該是不可見的,在當頁面跳回來或者切到前臺時Fragment應(yīng)該是可見的唬格,而這些ViewPager不會給出相應(yīng)的狀態(tài)家破,所以通過下面代碼就可以搞定:
@CallSuper
@Override
public void onStart() {
super.onStart();
Log.e("LazyLoadFragment", "onStart -> mIsRealViewSetup: " + mIsRealViewSetup + " , originVisibleOfUserHint+ " + originVisibleOfUserHint + " ]]> " + getClass().getSimpleName());
if (mIsRealViewSetup && originVisibleOfUserHint) {
this.onFragmentVisibilityChanged(true, true);
}
}
@CallSuper
@Override
public void onStop() {
super.onStop();
if (mIsRealViewSetup && originVisibleOfUserHint) {
this.onFragmentVisibilityChanged(false, true);
}
}
- 有子集ViewPager嵌套時的處理
我們經(jīng)常會碰到這樣的情況,主頁面ViewPager有幾個Fragment购岗,而其中有的Fragment又通過ViewPager嵌入了二級Fragment頁面汰聋,面對這樣的情況,上面的處理還有點欠缺喊积,主要是因為這樣:ViewPager嵌入Fragment后烹困,在初始化后,會自動或者被動切換到一個tab下乾吻,這內(nèi)部也會調(diào)用public void setUserVisibleHint(...)
設(shè)置可見性髓梅,這里存在的問題是他們并不知道父Fragment的可見狀態(tài)。
那么我的需求正好是子Fragment可見時父Fragment的狀態(tài)也應(yīng)該可見才對绎签,那么面對這樣的需求枯饿,做了以下的處理:
/**
* fragment可見性變化時回調(diào)
*
* @param isVisibleToUser 當前fragment是否前臺可見
* @param isLifeCycle 當前fragment可見性是否由fragment生命周期變化引起 false: 由調(diào)用{@link #setUserVisibleHint}引起
*/
@CallSuper
private void onFragmentVisibilityChanged(boolean isVisibleToUser, boolean isLifeCycle) {
this.mCurrentFragmentVisibility = isVisibleToUser;
Fragment fragment = getParentFragment();
boolean fragmentVisible = false;
if (fragment instanceof LazyLoadFragment) {
//處理view非懶加載時有二級fragment viewpager情況第一次初始化時的問題
//這種情況下一級fragment不可見,但二級viewpager中fragment初始化后會自動設(shè)置二級fragment的可見性
fragmentVisible = ((LazyLoadFragment) fragment).mCurrentFragmentVisibility;
if (!fragmentVisible && isLifeCycle) {
return;
}
}
this.onFragmentVisibilityChanged(isVisibleToUser);
Log.e("LazyLoadFragment", "onFragmentVisibilityChanged -> isVisibleToUser: " + isVisibleToUser + " , isLifeCycle: " + isLifeCycle + " , [-" + getClass().getSimpleName() + "-]" + " , parent: " + fragment.getClass().getSimpleName() + " = " + fragmentVisible);
final ViewPager viewPager = this.setNestedViewPagerWithNestedFragment();
if (null != viewPager) {
this.handleNestedFragmentVisibilityWhenFragmentVisibilityChanged(viewPager, isVisibleToUser, isLifeCycle);
}
}
/**
* 處理在內(nèi)外層viewpager里的fragment初始化后引起fragment可見性不一致問題(尤其開啟了view懶加載后诡必,子viewpager并未處理外層fragment可見性)
* 這里的處理是:
* 1. 外層fragment不可見時奢方,它內(nèi)部的所有fragment都應(yīng)該不可見
* 2. 內(nèi)部fragment可見時,他所關(guān)聯(lián)的父fragment也應(yīng)該可見
*
* @param viewPager
* @param isVisible
* @param isLifeCycle
*/
private void handleNestedFragmentVisibilityWhenFragmentVisibilityChanged(final ViewPager viewPager, boolean isVisible, boolean isLifeCycle) {
Log.e("DEBUG", "onFragmentVisibilityChanged ---- ### " + isVisible + " , " + isLifeCycle);
if (null == viewPager || isLifeCycle) {
return;
}
final FragmentPagerAdapter adapter = ((FragmentPagerAdapter) viewPager.getAdapter());
if (isVisible == false) {
//不可見的情況下,子viewpager里的所有fragment都不應(yīng)該可見
final int size = adapter.getCount();
for (int i = 0; i < size; i++) {
Fragment fragment = adapter.getItem(i);
if (null == fragment) {
continue;
}
fragment.setUserVisibleHint(isVisible);
}
} else {
Log.e("DEBUG", "onFragmentVisibilityChanged ---- " + viewPager.getCurrentItem());
Fragment fragment = adapter.getItem(viewPager.getCurrentItem());
if (null != fragment) {
fragment.setUserVisibleHint(isVisible);
}
}
}
/**
* 適用與外層是viewpager(vp_outer) 蟋字,viewpager中某個fragment中也用了viewpager(vp_inner)時稿蹲,用來設(shè)置vp_inner
*
* @return
*/
protected ViewPager setNestedViewPagerWithNestedFragment() {
return null;
}
子類通過復(fù)寫protected ViewPager setNestedViewPagerWithNestedFragment()
方法配置關(guān)聯(lián)的ViewPager。
這樣愉老,懶加載過程就算完成了场绿,有問題就留言吧。