Android ViewPager+Fragment 懒加载最简
Fragment 懒加载介绍
Android ViewPager+Fragment 懒加载方案一(setUserVisibleHint())
框架.png日常项目中无可或缺的会用到ViewPager嵌套Fragment的操作,以及Fragment中再嵌套Fragment的这种复杂嵌套的操作.由于ViewPager的缓存机制就会加载多个Fragmen页面导致加载速度变慢,这个时候我们就用到了Fragment 懒加载操作了
参考 Android ViewPager+Fragment 懒加载方案一(setUserVisibleHint())方案咱们通过setUserVisibleHint()和onResume()方法搭配实现了Fragment的懒加载,但是咱们处理的是在onResume()中处理了懒加载的逻辑这背离了onResume()方法的初衷,我TM onResume()方法都执行了你还不是真正的加载,所以在Androidx中就优化了这个问题,由此就有了今天的这个帖子
方案二效果.gifAndroidx 下的懒加载
Google 在 Androidx 在 FragmentTransaction 中增加了 setMaxLifecycle 方法来控制 Fragment 所能调用的最大的生命周期函数
/**
* Set a ceiling for the state of an active fragment in this FragmentManager. If fragment is
* already above the received state, it will be forced down to the correct state.
*在此FragmentManager中为活动片段的状态设置上限。如果碎*片是已经高于接收状态,它将被强制降到正确的状态。
*
* <p>The fragment provided must currently be added to the FragmentManager to have it's
* Lifecycle state capped, or previously added as part of this transaction. If the
* {@link Lifecycle.State#INITIALIZED} is passed in as the {@link Lifecycle.State} and the
* provided fragment has already moved beyond {@link Lifecycle.State#INITIALIZED}, an
* {@link IllegalArgumentException} will be thrown.</p>
*
* <p>If the {@link Lifecycle.State#DESTROYED} is passed in as the {@link Lifecycle.State} an
* {@link IllegalArgumentException} will be thrown.</p>
*
* @param fragment the fragment to have it's state capped.
* @param state the ceiling state for the fragment.
* @return the same FragmentTransaction instance
*/
@NonNull
public FragmentTransaction setMaxLifecycle(@NonNull Fragment fragment,
@NonNull Lifecycle.State state) {
addOp(new Op(OP_SET_MAX_LIFECYCLE, fragment, state));
return this;
}
此方法的意思是 可以在此FragmentManager设置活跃状态下Fragment的最大状态,如果该Fragment超过了设置的最大状态,那么会强制将Fragment降级到正确的状态。
所以我们只要处理设置这个setMaxLifecycle()方法就能做到懒加载的状态了
在Androidx下,FragmentPagerAdapter、FragmentStatePagerAdapter 类新增了含有behavior的字段的构造函数,并舍弃了FragmentPagerAdapter(@NonNull FragmentManager fm)方法。
/**
* Constructor for {@link FragmentStatePagerAdapter} that sets the fragment manager for the
* adapter. This is the equivalent of calling
* {@link #FragmentStatePagerAdapter(FragmentManager, int)} and passing in
* {@link #BEHAVIOR_SET_USER_VISIBLE_HINT}.
*
* <p>Fragments will have {@link Fragment#setUserVisibleHint(boolean)} called whenever the
* current Fragment changes.</p>
*
* @param fm fragment manager that will interact with this adapter
* @deprecated use {@link #FragmentStatePagerAdapter(FragmentManager, int)} with
* {@link #BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT}
*/
@Deprecated
public FragmentStatePagerAdapter(@NonNull FragmentManager fm) {
this(fm, BEHAVIOR_SET_USER_VISIBLE_HINT);
}
/**
* Constructor for {@link FragmentStatePagerAdapter}.
*
* If {@link #BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT} is passed in, then only the current
* Fragment is in the {@link Lifecycle.State#RESUMED} state, while all other fragments are
* capped at {@link Lifecycle.State#STARTED}. If {@link #BEHAVIOR_SET_USER_VISIBLE_HINT} is
* passed, all fragments are in the {@link Lifecycle.State#RESUMED} state and there will be
* callbacks to {@link Fragment#setUserVisibleHint(boolean)}.
*
* @param fm fragment manager that will interact with this adapter
* @param behavior determines if only current fragments are in a resumed state
*/
public FragmentStatePagerAdapter(@NonNull FragmentManager fm,
@Behavior int behavior) {
mFragmentManager = fm;
mBehavior = behavior;
}
其中 Behavior 的类型如下:
Behavior 源码.png
/**
* Indicates that {@link Fragment#setUserVisibleHint(boolean)} will be called when the current
* fragment changes.
*
* @deprecated This behavior relies on the deprecated
* {@link Fragment#setUserVisibleHint(boolean)} API. Use
* {@link #BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT} to switch to its replacement,
* {@link FragmentTransaction#setMaxLifecycle}.
* @see #FragmentStatePagerAdapter(FragmentManager, int)
*/
@Deprecated
public static final int BEHAVIOR_SET_USER_VISIBLE_HINT = 0;
/**
* Indicates that only the current fragment will be in the {@link Lifecycle.State#RESUMED}
* state. All other Fragments are capped at {@link Lifecycle.State#STARTED}.
*
* @see #FragmentStatePagerAdapter(FragmentManager, int)
*/
public static final int BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT = 1;
- BEHAVIOR_SET_USER_VISIBLE_HINT:当 Fragment 对用户的可见状态发生改变时,setUserVisibleHint 方法会被调用。
- BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT:那么当前选中的 Fragment 在 Lifecycle.State#RESUMED 状态 ,其他不可见的 Fragment 会被限制在 Lifecycle.State#STARTED 状态。
这里我们先探究性ViewPager 加载机制 vp.setAdapter()-> requestLayout()->ViewPager.onMeasure()->ViewPager.populate() ->ViewPager.populate(int newCurrentItem) -> adapter.setPrimaryItem()
@Override
@SuppressWarnings({"ReferenceEquality", "deprecation"})
public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
Fragment fragment = (Fragment)object;
// 判断是否是当前的 Fragment
if (fragment != mCurrentPrimaryItem) {
if (mCurrentPrimaryItem != null) {
mCurrentPrimaryItem.setMenuVisibility(false);
//判断 Behavior 类型
if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
mCurTransaction.setMaxLifecycle(mCurrentPrimaryItem, Lifecycle.State.STARTED);
} else {
mCurrentPrimaryItem.setUserVisibleHint(false);
}
}
fragment.setMenuVisibility(true);
//判断 Behavior 类型
if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
//是设置新类型走方案二 设置当前Fragment为最高状态其他的Fragment强制降低状态 设置当前为RESUMED状态 执行onResume()
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.RESUMED);
} else {
//不是默认类型走方案一 设置Fragment 可见
fragment.setUserVisibleHint(true);
}
mCurrentPrimaryItem = fragment;
}
}
由此可以得出我们只需要在FragmentStatePagerAdapter()创建的时候设置 BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT 类型的behavior 就可以了
实现:
1.ViewPager 的Adapter
package com.wu.material.adapter
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentStatePagerAdapter
/**
*
* 作者:吴奎庆
*
* 时间:2021/11/13
*
* 用途:方案二的 Adapter
*/
class FragmentAdapter2(fragmentList:ArrayList<Fragment>, fm: FragmentManager): FragmentStatePagerAdapter (fm,BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT){
var fragmentList=ArrayList<Fragment>()
init {
this.fragmentList=fragmentList
}
override fun getItem(position: Int): Fragment {
return fragmentList.get(position)
}
override fun getCount(): Int {
return fragmentList.size
}
}
2.Fragment 代码
package com.wu.material.fragment
import android.content.Context
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import com.wu.material.R
/**
* @author wkq
*
* @date 2022年01月30日 14:09
*
*@des
*
*/
class DemoFragment1 : Fragment() {
companion object {
fun newInstance(): DemoFragment1 {
val args = Bundle()
val fragment = DemoFragment1()
fragment.arguments = args
return fragment
}
}
// Fragment 完整生命周期:onAttach -> onCreate -> onCreatedView -> onActivityCreated -> onStart -> onResume ->
// onPause -> onStop -> onDestroyView -> onDestroy -> onDetach
override fun onAttach(context: Context) {
super.onAttach(context)
Log.e("DemoFragment1:","onAttach()")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.e("DemoFragment1:","onCreate()")
}
override fun onHiddenChanged(hidden: Boolean) {
super.onHiddenChanged(hidden)
Log.e("DemoFragment1:","onHiddenChanged()"+hidden)
}
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
super.setUserVisibleHint(isVisibleToUser)
Log.e("DemoFragment1:","setUserVisibleHint()"+isVisibleToUser)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
Log.e("DemoFragment1:","onCreateView()")
return inflater.inflate(R.layout.page_1, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Log.e("DemoFragment1:","onViewCreated()")
}
override fun onStart() {
super.onStart()
Log.e("DemoFragment1:","onStart()")
}
override fun onResume() {
super.onResume()
Toast.makeText(activity,"DemoFragment1懒加载加载数据", Toast.LENGTH_SHORT).show()
Log.e("DemoFragment1:","onResume()")
}
override fun onPause() {
super.onPause()
Log.e("DemoFragment1:","onPause()")
}
override fun onStop() {
super.onStop()
Log.e("DemoFragment1:","onStop()")
}
override fun onDestroyView() {
super.onDestroyView()
Log.e("DemoFragment1:","onDestroyView()")
}
override fun onDestroy() {
super.onDestroy()
Log.e("DemoFragment1:","onDestroy()")
}
override fun onDetach() {
super.onDetach()
Log.e("DemoFragment1:","onDetach()")
}
}
3.Activity代码
package com.wu.material.activity
import android.content.Context
import android.graphics.Color
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import com.wu.material.R
import com.wu.material.adapter.FragmentAdapter
import com.wu.material.adapter.FragmentAdapter2
import com.wu.material.databinding.ActivityLazyLoadingBinding
import com.wu.material.fragment.*
import com.wu.material.widget.CustomTitleView
import net.lucode.hackware.magicindicator.ViewPagerHelper
import net.lucode.hackware.magicindicator.buildins.commonnavigator.CommonNavigator
import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.CommonNavigatorAdapter
import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerIndicator
import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerTitleView
import net.lucode.hackware.magicindicator.buildins.commonnavigator.indicators.LinePagerIndicator
/**
* @author wkq
*
* @date 2022年03月16日 9:04
*
*@des 懒加载方案二 (AndroidX)
*
*/
class LazyLoadingFragmentActivity2 : AppCompatActivity() {
var binding: ActivityLazyLoadingBinding? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView<ActivityLazyLoadingBinding>(this, R.layout.activity_lazy_loading)
initVp()
initMagicIndicator()
}
private fun initMagicIndicator() {
var mTitles = arrayOf("当前", "历史", "曾经", "未来")
binding!!.magicIndicator.setBackgroundColor(Color.WHITE)
val commonNavigator = CommonNavigator(this)
commonNavigator.scrollPivotX = 0.35f
commonNavigator.adapter = object : CommonNavigatorAdapter() {
override fun getCount(): Int {
return if (mTitles == null) 0 else mTitles.size
}
override fun getTitleView(context: Context, index: Int): IPagerTitleView {
val simplePagerTitleView = CustomTitleView(context)
simplePagerTitleView.setText(mTitles.get(index))
simplePagerTitleView.normalColor = Color.parseColor("#666666")
simplePagerTitleView.selectedColor = Color.parseColor("#222222")
simplePagerTitleView.setOnClickListener { binding!!.vpContent.setCurrentItem(index) }
return simplePagerTitleView
}
override fun getIndicator(context: Context): IPagerIndicator {
val indicator = LinePagerIndicator(context)
indicator.mode = LinePagerIndicator.MODE_EXACTLY
indicator.setColors(Color.parseColor("#3399FF"))
indicator.setRoundRadius(5f)
return indicator
}
}
binding!!.vpContent.offscreenPageLimit
binding!!.magicIndicator.navigator = commonNavigator
ViewPagerHelper.bind(binding!!.magicIndicator, binding!!.vpContent)
}
private fun initVp() {
var fragmentList = ArrayList<Fragment>()
fragmentList.add(DemoFragment1.newInstance())
fragmentList.add(DemoFragment2.newInstance())
fragmentList.add(DemoFragment3.newInstance())
fragmentList.add(DemoFragment4.newInstance())
var fragmentAdapter = FragmentAdapter2(fragmentList, this.supportFragmentManager)
this.binding!!.vpContent.adapter = fragmentAdapter
}
}
总结
由ViewPager +FragmentStateAdapter+Fragment 实现复杂页面的嵌效果,由于方案一与onResume()方法的设计初衷有背,所以Google官方在Androidx中做了优化,基于setMaxLifecycle()方法设置Fragment的状态 提供这种更方便实现懒加载的方案