开发Android延整1

Android ViewPager+Fragment 懒加载最简

2022-03-17  本文已影响0人  没有了遇见
效果.png

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中就优化了这个问题,由此就有了今天的这个帖子

方案二效果.gif

Androidx 下的懒加载

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;

这里我们先探究性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的状态 提供这种更方便实现懒加载的方案

看都看了点个赞再走吧!!!

1.源码

上一篇下一篇

猜你喜欢

热点阅读