Fragment与ViewPager的配合使用

2021-06-29  本文已影响0人  乌托邦式的爱情

在上一篇我们介绍了Fragment的基本使用,除了介绍的常规的静态加载和动态加载方式之外,其实Fragment更多的时候是与ViewPager配合使用,今天我们就来看看Fragment与ViewPager到底是怎么去使用的。

FragmentPagerAdapter与FragmentStatePagerAdapter

在使用ViewPager的时候,必不可少需要接触的就是PagerAdapter,而对于Fragment+ViewPager来说,系统提供了FragmentPagerAdapter和FragmentStatePagerAdapter。
FragmentPagerAdapter和FragmentStatePagerAdapter都是用来适配Fragment+ViewPager的,那么,二者有什么不同呢?

(1)使用的场景不同:FragmentPagerAdapter适用于装载的fragment数量较少,且页面不怎么变动的情况;FragmentStatePagerAdapter适用于装载的fragment数量较多,且页面加载的数据量很多,页面变化很大的情况。

(2)内存使用不同:FragmentPagerAdapter加载好后的fragment在fragment滑动的时候,系统只会调用detach()方法将UI从页面移除,而Fragment的实例依然会保留,由FragmentManager来管理,不会销毁Fragment的实例。而使用FragmentStatePagerAdapter系统会调用remove()方法将fragment的实例进行销毁。

Fragment懒加载

在日常开发的时候,我们知道当我们在onCreate方法里面如果做了太多请求网络数据的操作,会导致页面出现会比较慢,更多的时候是希望展示哪个页面的时候才去加载那个页面的数据,其他没有被展示出来的页面就不要去加载数据了,只有当页面显示的时候才去加载数据,而这也可以被理解为懒加载机制,即我需要的时候才加载显示。那么,在弄懂Fragment懒加载的时候,我们首先需要去了解Fragment的生命周期的执行情况。

使用FragmentPagerAdapter方式:
默认加载第一页


1.png

执行的生命周期的情况为:
(1)执行第一个Fragment的setUserVisibleHint,返回值为false。
(2)执行第二个Fragment的setUserVisibleHint,返回值为false。
(3)执行第一个Fragment的setUserVisibleHint,返回值为true。
(4)执行第一个Fragment的onAttach->onCreate。
(5)执行第二个Fragment的onAttach->onCreate。
(6)执行第一个Fragment的onCreateView->onActivityCreated->onStart->onResume。
(7)执行第二个Fragment的onCreateView->onActivityCreated->onStart->onResume。

默认加载第二页

2.png

执行的生命周期情况为:

(1)执行第二个Fragment的setUserVisibleHint,返回值为false。
(2)执行第一个Fragment的setUserVisibleHint,返回值为false。
(3)执行第三个Fragment的setUserVisibleHint,返回值为false。
(4)执行第二个Fragment的setUserVisibleHint,返回值为true。
(5)执行第二个Fragment的onAttach->onCreate。
(6)执行第一个Fragment的onAttach->onCreate。
(7)执行第三个Fragment的onAttach->onCreate。
(8)执行第二个Fragment的onCreateView->onActivityCreated->onStart->onResume。
(9)执行第一个Fragment的onCreateView->onActivityCreated。
(10)执行第三个Fragment的onCreateView->onActivityCreated。
(11)执行第一个Fragment的onStart->onResume。
(12)执行第三个Fragment的onStart->onResume。

从第一页滑到第二页

3.png

执行的生命周期情况为:

(1)执行第三个Fragment的setUserVisibleHint,返回值为false。
(2)执行第一个Fragment的setUserVisibleHint,返回值为false。
(3)执行第二个Fragment的setUserVisibleHint,返回值为true。
(4)执行第三个Fragment的onAttach->onCreate->onCreateView->onActivityCreated->onStart->onResume。

从第二页滑到第三页:

4.png

执行的生命周期的情况:

(1)执行第四个Fragment的setUserVisibleHint,返回值为false。
(2)执行第二个Fragment的setUserVisibleHint,返回值为false。
(3)执行第三个Fragment的setUserVisibleHint,返回值为true。
(4)执行第四个Fragment的onAttach->onCreate。
(5)执行第一个Fragment的onPause->onStop->onDestroyView。
(6)执行第四个Fragment的onCreateView->onActivityCreated->onStart->onResume。

从第三页滑到第四页:


5.png

执行的生命周期的情况:

(1)执行第三个Fragment的setUserVisibleHint,返回值为false。
(2)执行第四个Fragment的setUserVisibleHint,返回值为true。
(3)执行第二个Fragment的onPause->onStop->onDestroyView。

从第四页滑到到第三页:


6.png

执行的生命周期情况为:

(1)执行第二个Fragment的setUserVisibleHint,返回值为false。
(2)执行第四个Fragment的setUserVisibleHint,返回值为false。
(3)执行第三个Fragment的setUserVisibleHint,返回值为false。
(4)执行第二个Fragment的onCreateView->onActivityCreated->onStart->onResume。

总结一下:

(1)首次进入的时候,fragment的setUserVisibleHint会首先调用,执行的顺序为目标Fragment(返回值为false)->目标Fragment左边相邻(返回值为false)->目标Fragment右边相邻(返回值为false)->目标Fragment(返回值为true),如果左边或者右边没有Fragment,则不执行。

(2)Fragment滑动的时候,从左向右滑,先执行右边Fragment的setUserVisibleHint(返回值为false),再执行左边Fragment的setUserVisibleHint(返回值为false),最后执行显示Fragment的setUserVisibleHint(返回值为true)。从右向左滑,先执行左边Fragment的setUserVisibleHint(返回值为false),再执行右边Fragment的setUserVisibleHint(返回值为false),最后执行显示Fragment的setUserVisibleHint(返回值为true),如果左边或者右边没有Fragment,则不执行。

(3)Fragment的实例不会被销毁,当前展示的Fragment左右的Fragment的视图不会被销毁,其他未展示的Fragment的视图会被销毁。

(4)加载数据的时候可以通过setUserVisibleHint来进行处理。

(5)一般清况下有三个Fragment处于运行状态,一个位于前台活动,左右两个处于后台活动。

使用FragmentStatePagerAdapter方式:

默认加载第一页


1.png

执行的生命周期的情况为:

(1)执行第一个Fragment的setUserVisibleHint,返回值为false。
(2)执行第二个Fragment的setUserVisibleHint,返回值为false。
(3)执行第一个Fragment的setUserVisibleHint,返回值为true。
(4)执行第一个Fragment的onAttach->onCreate。
(5)执行第二个Fragment的onAttach->onCreate。
(6)执行第一个Fragment的onCreateView->onActivityCreated->onStart->onResume。
(7)执行第二个Fragment的onCreateView->onActivityCreated->onStart->onResume。

默认加载第二页


7.png

执行的生命周期情况为:

(1)执行第二个Fragment的setUserVisibleHint,返回值为false。
(2)执行第一个Fragment的setUserVisibleHint,返回值为false。
(3)执行第三个Fragment的setUserVisibleHint,返回值为false。
(4)执行第二个Fragment的setUserVisibleHint,返回值为true。
(5)执行第二个Fragment的onAttach->onCreate。
(6)执行第一个Fragment的onAttach->onCreate。
(7)执行第三个Fragment的onAttach->onCreate。
(8)执行第二个Fragment的onCreateView->onActivityCreated->onStart->onResume。
(9)执行第一个Fragment的onCreateView->onActivityCreated。
(10)执行第三个Fragment的onCreateView->onActivityCreated->onStart->onResume。
(11)执行第一个Fragment的onStart->onResume。

从第一页滑到第二页


8.png

执行的生命周期情况为:

(1)执行第三个Fragment的setUserVisibleHint,返回值为false。
(2)执行第一个Fragment的setUserVisibleHint,返回值为false。
(3)执行第二个Fragment的setUserVisibleHint,返回值为true。
(4)执行第三个Fragment的onAttach->onCreate->onCreateView->onActivityCreated->onStart->onResume。

从第二页滑到第三页:


9.png

执行的生命周期情况为:

(1)执行第四个Fragment的setUserVisibleHint,返回值为false。
(2)执行第二个Fragment的setUserVisibleHint,返回值为false。
(3)执行第三个Fragment的setUserVisibleHint,返回值为true。
(4)执行第四个Fragment的onAttach->onCreate。
(5)执行第一个Fragment的onPause->onStop->onDestroyView->onDestroy->onDetach。
(6)执行第四个Fragment的onCreateView->onActivityCreated->onStart->onResume。

需要强调的是:当我们创建第四个Fragment的时候,第一个Fragment就会被销毁实例,而在使用FragmentPagerAdapter的时候只会销毁视图。

从第三页滑到第四页:


10.png

执行的生命周期情况为:

(1)执行第三个Fragment的setUserVisibleHint,返回值为false。
(2)执行第四个Fragment的setUserVisibleHint,返回值为true。
(3)执行第二个Fragment的onPause->onStop->onDestroyView->onDestroy->onDetach。

从第四页滑到到第三页:


11.png

执行的生命周期情况为:

(1)执行第二个Fragment的setUserVisibleHint,返回值为false。
(2)执行第四个Fragment的setUserVisibleHint,返回值为false。
(3)执行第四个Fragment的setUserVisibleHint,返回值为true。
(4)执行第二个Fragment的onAttach->onCreate->onCreateView->onActivityed->onStart->onResume。

总结一下:

(1)首次进入的时候,fragment的setUserVisibleHint会首先调用,执行的顺序为目标Fragment(返回值为false)->目标Fragment左边相邻(返回值为false)->目标Fragment右边相邻(返回值为false)->目标Fragment(返回值为true),如果左边或者右边没有Fragment,则不执行。

(2)Fragment滑动的时候,从左向右滑,先执行右边Fragment的setUserVisibleHint(返回值为false),再执行左边Fragment的setUserVisibleHint(返回值为false),最后执行显示Fragment的setUserVisibleHint(返回值为true)。从右向左滑,先执行左边Fragment的setUserVisibleHint(返回值为false),再执行右边Fragment的setUserVisibleHint(返回值为false),最后执行显示Fragment的setUserVisibleHint(返回值为true),如果左边或者右边没有Fragment,则不执行。

(3)Fragment的实例会被销毁,当前展示的Fragment左右的Fragment的视图不会被销毁,其他未展示的Fragment的实例会被销毁。

(4)加载数据的时候可以通过setUserVisibleHint来进行处理。

(5)一般清况下有三个Fragment处于运行状态,一个位于前台活动,左右两个处于后台活动。

其实最后我们很轻清楚的看到,使用FragmentPagerAdapter和FragmentStatePagerAdapter最后的区别就在于Fragment的实例是否被回收,而其他的用法都是一样的,现在我们该知道在日常的开发过程中到底该怎么去选择对应的方式了吧。

懒加载的具体写法:

@Override
public void onResume() {
    super.onResume();
    if (!isLoadData && getUserVisibleHint()) {
        isLoadData = true;
        requestNetData(1);
    }
}

其中isLoadData是我们声明的变量,代表是否是第一次加载。

Fragment与ViewPager实战

1、创建布局文件

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".fragment.BlankFragmentActivity">

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tab_layout"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tab_layout" />


</androidx.constraintlayout.widget.ConstraintLayout>

2、写好我们的适配器

/**
 * author: zhoufan
 * data: 2021/6/28 10:18
 * content:
 */
class BlankFragmentAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) {

    private var mList: List<Fragment>? = null
    private var mTitleList: List<String>? = null

    constructor(fm: FragmentManager, list: List<Fragment>, titleList: List<String>) : this(fm) {
        mList = list
        mTitleList = titleList
    }


    /**
     * 加载的Fragment的数量
     */
    override fun getCount(): Int = mList!!.size


    /**
     * 获取当前显示的Fragment
     */
    override fun getItem(position: Int): Fragment {
        return mList!![position]
    }

    /**
     * Fragment移除的时候
     */
    override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
        super.destroyItem(container, position, `object`)
    }

    /**
     * 获取当前显示的Fragment的位置
     */
    override fun getItemPosition(`object`: Any): Int {
        return `object` as Int
    }

    /**
     * Fragment对应的Title
     */
    override fun getPageTitle(position: Int): CharSequence? {
        return mTitleList!![position]
    }
}

3、直接加载关联

val titleList = mutableListOf("第一页", "第二页", "第三页", "第四页")
val fragmentList = mutableListOf<Fragment>()
for (i in 0 until titleList.size) {
    val fragment = BlankFragment.newInstance(i.toString())
    fragmentList.add(fragment)
    tab_layout.addTab(tab_layout.newTab().setText(titleList[i]))
}
val adapter = BlankFragmentAdapter(supportFragmentManager,fragmentList,titleList)
view_pager.adapter = adapter
tab_layout.setupWithViewPager(view_pager)

4、对应的Fragment

private const val ARG_PARAM1 = "param1"
private const val TAG = "stevens"

/**
 * 空白的Fragment
 */
class BlankFragment : Fragment() {

    private var param1: String? = null
    private var isLoadData = false

    companion object {
        @JvmStatic
        fun newInstance(param1: String) =
            BlankFragment().apply {
                arguments = Bundle().apply {
                    putString(ARG_PARAM1, param1)
                }
            }
    }

    override fun setUserVisibleHint(isVisibleToUser: Boolean) {
        super.setUserVisibleHint(isVisibleToUser)
        if (param1 == null) {
            arguments?.let {
                param1 = it.getString(ARG_PARAM1)
            }
        }
        Log.i(TAG, "$param1 :BlankFragment:setUserVisibleHint   $isVisibleToUser")
    }

    override fun onInflate(context: Context, attrs: AttributeSet, savedInstanceState: Bundle?) {
        super.onInflate(context, attrs, savedInstanceState)
        Log.i(TAG, "$param1 :BlankFragment:onInflate")
    }

    override fun onAttach(context: Context) {
        super.onAttach(context)
        Log.i(TAG, "$param1 :BlankFragment:onAttach")
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.i(TAG, "$param1 :BlankFragment:onCreate")
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        Log.i(TAG, "$param1 :BlankFragment:onCreateView")
        return inflater.inflate(R.layout.fragment_blank, container, false)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        Log.i(TAG, "$param1 :BlankFragment:onActivityCreated")
    }

    override fun onStart() {
        super.onStart()
        Log.i(TAG, "$param1 :BlankFragment:onStart")
    }

    override fun onResume() {
        super.onResume()
        Log.i(TAG, "$param1 :BlankFragment:onResume")
    }

    override fun onPause() {
        super.onPause()
        Log.i(TAG, "$param1 :BlankFragment:onPause")
        if (!isLoadData && userVisibleHint) {
            isLoadData = true
            // 在这里加载数据
        }
    }

    override fun onStop() {
        super.onStop()
        Log.i(TAG, "$param1 :BlankFragment:onStop")
    }

    override fun onDestroyView() {
        super.onDestroyView()
        Log.i(TAG, "$param1 :BlankFragment:onDestroyView")
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.i(TAG, "$param1 :BlankFragment:onDestroy")
    }

    override fun onDetach() {
        super.onDetach()
        Log.i(TAG, "$param1 :BlankFragment:onDetach")
    }
}

到这里,一个简单的Fragment+ViewPager就完成了。

上一篇下一篇

猜你喜欢

热点阅读