整個原理是把真實(shí)需要加載的Fragment包裝到一個空LazyFragment里晤斩,通過控制LazyFragment來實(shí)現(xiàn)對真實(shí)Fragment的加載缸浦。具體效果就是需要惰性加載的類不需要改繼承類纺荧,基本不用做任何修改晴埂,用LazyFragment包裝下就能用痕囱。不關(guān)心具體原理的話請直接參看源碼:
https://github.com/aesean/LazyLoadFragment
Fragment惰性算是非常常用的一個功能了诞吱。之前我們惰性加載也只是用在ViewPager里舟奠,然后配合setUserVisibleHint可以實(shí)現(xiàn)惰性加載竭缝。如果用Google搜索的話可能會找到類似下面的實(shí)現(xiàn)。
實(shí)現(xiàn)原理都是通過setUserVisibleHint回調(diào)沼瘫,然后在onCreateView做判斷選擇加載的布局抬纸。但是注意setUserVisibleHint是需要用戶自己主動去處理的,F(xiàn)ragment本身是不會去維護(hù)它的耿戚。也就是說除非有類似FragmentPagerAdater里的方式那樣主動去維護(hù)之外湿故,getUserVisibleHint永遠(yuǎn)都是返回true。
@Override
public Object instantiateItem(ViewGroup container, int position) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
final long itemId = getItemId(position);
// Do we already have this fragment?
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
if (fragment != null) {
if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
mCurTransaction.attach(fragment);
} else {
fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
mCurTransaction.add(container.getId(), fragment,
makeFragmentName(container.getId(), itemId));
}
if (fragment != mCurrentPrimaryItem) {
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
}
return fragment;
}
搜到的文章基本都是通過復(fù)寫setUserVisibleHint膜蛔,然后通過接受到的參數(shù)在onCreateView里處理加載坛猪。這樣當(dāng)然是可以實(shí)現(xiàn)的。但是這種處理方式有個很麻煩的問題皂股。這需要所有需要有類似特性的Fragment需要繼承寫好的基類墅茉。通常這可能沒什么問題,除了一些類似DialogFragment這種特殊情況之外屑墨。另外Fragment的生命周期會受到影響躁锁。可能很多人會通過Fragment的onViewCreate來初始化控件引用卵史。這種寫法會有個很麻煩的問題就是需要判斷當(dāng)前是惰性加載還是正常加載战转。因?yàn)槎栊约虞d和正常加載時(shí)候的View是不一樣的。然后相當(dāng)于是Fragment所有的生命周期可能都需要判斷當(dāng)前是惰性加載還是正常加載了以躯。如果再加上需要處理的類非常多的話槐秧,這里就要非常頭疼了。
那有沒有一種簡便的實(shí)現(xiàn)方式忧设?最好是以前寫好的類不做任何改動刁标,直接就能繼續(xù)用的。
我們其實(shí)只是希望某個Fragment實(shí)現(xiàn)惰性加載址晕,那我們讓目標(biāo)Fragment不動膀懈,在需要加載這個Fragment的地方先加載一個寫好的空Fragment,等需要的時(shí)候在load真實(shí)的Fragment谨垃,這樣原Fragment就無需做任何修改了启搂。
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mOnCreateView = true;
return inflater.inflate(R.layout.fragment_lazy, container, false);
}
private void clearRootContainer() {
mLazyRootContainer.removeAllViews();
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mLazyRootContainer = (ViewGroup) view.findViewById(R.id.lazy_root_container);
if (isFirstVisible && getUserVisibleHint()) {
loadLazyFragment();
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
mOnCreateView = false;
isFirstVisible = true;
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (mOnCreateView && getUserVisibleHint() && isFirstVisible) {
loadLazyFragment();
}
}
private void loadLazyFragment() {
clearRootContainer();
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
transaction.replace(R.id.lazy_root_container, getLazyFragmentLoader());
transaction.commit();
isFirstVisible = false;
}
默認(rèn)加載的空Fragment如果你需要可以添加一些進(jìn)度條之類的。由于LazyFragment需要知道自己要真實(shí)加載的Fragment是什么刘陶。所以這里創(chuàng)建一個接口
public interface ILazyFragmentLoader {
Fragment createFragment(Context context);
}
然后把這個接口通過Bundle參數(shù)傳遞進(jìn)來胳赌。通過Bundle傳遞就必須實(shí)現(xiàn)序列化接口。
static Bundle createBundle(Bundle bundle, ILazyFragmentLoader target) {
if (bundle == null) {
bundle = new Bundle();
}
if (target instanceof Parcelable) {
bundle.putParcelable(KEY_LAZY_FRAGMENT_LOADER, (Parcelable) target);
} else if (target instanceof Serializable) {
bundle.putSerializable(KEY_LAZY_FRAGMENT_LOADER, (Serializable) target);
} else {
throw new IllegalArgumentException("ILazyFragmentLoader 必須實(shí)現(xiàn)Serializable或者Parcelable");
}
return bundle;
}
這里其實(shí)實(shí)現(xiàn)Parcelable和Serializable中的一個就可以了匙隔。這里注意bundle只實(shí)現(xiàn)了Parcelable接口疑苫,沒有實(shí)現(xiàn)Serializable,所以如果自定義的ILazyFragmentLoader實(shí)現(xiàn)類如果需要用到Bundle,一定要實(shí)現(xiàn)Parcelable捍掺。
public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
......
}
修改后的最終效果就是
public static LazyFragment newLazyInstance(String title) {
return LazyFragment.newInstance(new LazyFragmentLoader(SampleFragment.class, newArguments(title)));
}
SampleFragment.class是真實(shí)需要處理的Fragment撼短,newArguments是個Bundle對象是需要給SampleFragment傳遞的參數(shù)。SampleFragment被包裝成了LazyFragment挺勿,然后把LazyFragment當(dāng)成SampleFragment使用即可阔加。如果SampleFragment被用在FragmentViewPager里這里就會自動實(shí)現(xiàn)惰性加載,因?yàn)镕ragmentViewPager處理了setUserVisibleHint满钟。如果是希望手動控制加載處理setUserVisibleHint方法即可。具體可以參考ClickLoadLazyFragment的代碼胳喷。