Use of Fragment with ViewPager

Posted by bridawg on Thu, 27 Jan 2022 05:47:41 +0100

In the last article, we introduced the basic use of Fragment. In addition to the conventional static loading and dynamic loading methods, Fragment is often used with ViewPager. Today, let's take a look at how Fragment and ViewPager are used.

FragmentPagerAdapter and FragmentStatePagerAdapter

When using ViewPager, the PagerAdapter is indispensable. For Fragment+ViewPager, the system provides FragmentPagerAdapter and FragmentStatePagerAdapter.
FragmentPagerAdapter and FragmentStatePagerAdapter are used to adapt to Fragment+ViewPager. What is the difference between them?

(1) Different scenarios are used: the FragmentPagerAdapter is applicable to the situation that the number of fragments loaded is small and the page does not change much; The FragmentStatePagerAdapter is applicable to situations where a large number of fragments are loaded, a large amount of data is loaded on the page, and the page changes greatly.

(2) Different memory usage: when the fragment loaded by the FragmentPagerAdapter slides, the system will only call the detach() method to remove the UI from the page, while the fragment instance will still be retained and managed by the FragmentManager without destroying the fragment instance. Using the FragmentStatePagerAdapter, the system will call the remove() method to destroy the instance of the fragment.

Fragment lazy loading

In daily development, we know that if we do too many operations to request network data in onCreate method, the page will appear slowly. More often, we want to load the data of that page when we want to display it, and do not load the data of other pages that are not displayed, Load data only when the page is displayed, which can also be understood as lazy loading mechanism, that is, load the display when I need it. Then, when understanding the lazy loading of Fragment, we first need to understand the implementation of the Fragment life cycle.

Use the FragmentPagerAdapter method:
Load first page by default

The life cycle of execution is:
(1) Execute setUserVisibleHint of the first Fragment, and the return value is false.
(2) Execute setUserVisibleHint of the second Fragment, and the return value is false.
(3) Execute setUserVisibleHint of the first Fragment, and the return value is true.
(4) Execute onattach - > oncreate of the first Fragment.
(5) Execute onattach - > oncreate of the second Fragment.
(6) Execute oncreateview - > onactivitycreated - > OnStart - > onresume of the first Fragment.
(7) Execute oncreateview - > onactivitycreated - > OnStart - > onresume of the second Fragment.

The second page is loaded by default

The life cycle of execution is:
(1) Execute setUserVisibleHint of the second Fragment, and the return value is false.
(2) Execute setUserVisibleHint of the first Fragment, and the return value is false.
(3) Execute setUserVisibleHint of the third Fragment, and the return value is false.
(4) Execute setUserVisibleHint of the second Fragment, and the return value is true.
(5) Execute onattach - > oncreate of the second Fragment.
(6) Execute onattach - > oncreate of the first Fragment.
(7) Execute onattach - > oncreate of the third Fragment.
(8) Execute oncreateview - > onactivitycreated - > OnStart - > onresume of the second Fragment.
(9) Execute oncreateview - > onactivitycreated of the first Fragment.
(10) Execute oncreateview - > onactivitycreated of the third Fragment.
(11) Execute OnStart - > onresume of the first Fragment.
(12) Execute OnStart - > onresume of the third Fragment.

Slide from page 1 to page 2

The life cycle of execution is:
(1) Execute setUserVisibleHint of the third Fragment, and the return value is false.
(2) Execute setUserVisibleHint of the first Fragment, and the return value is false.
(3) Execute setUserVisibleHint of the second Fragment, and the return value is true.
(4) Execute onattach - > oncreate - > oncreateview - > onactivitycreated - > OnStart - > onresume of the third Fragment.

Slide from page 2 to page 3:

Life cycle of execution:
(1) Execute setUserVisibleHint of the fourth Fragment, and the return value is false.
(2) Execute setUserVisibleHint of the second Fragment, and the return value is false.
(3) Execute setUserVisibleHint of the third Fragment, and the return value is true.
(4) Execute onattach - > oncreate of the fourth Fragment.
(5) Execute onpause - > onstop - > ondestroyview of the first Fragment.
(6) Execute oncreateview - > onactivitycreated - > OnStart - > onresume of the fourth Fragment.

Slide from page 3 to page 4:

Life cycle of execution:
(1) Execute setUserVisibleHint of the third Fragment, and the return value is false.
(2) Execute setUserVisibleHint of the fourth Fragment, and the return value is true.
(3) Execute onpause - > onstop - > ondestroyview of the second Fragment.

Slide from page 4 to page 3:

The life cycle of execution is:
(1) Execute setUserVisibleHint of the second Fragment, and the return value is false.
(2) Execute setUserVisibleHint of the fourth Fragment, and the return value is false.
(3) Execute setUserVisibleHint of the third Fragment, and the return value is false.
(4) Execute oncreateview - > onactivitycreated - > OnStart - > onresume of the second Fragment.

To sum up:
(1) When entering for the first time, setUserVisibleHint of the fragment will be called first. The order of execution is target fragment (return value is false) - > adjacent to the left of the target fragment (return value is false) - > adjacent to the right of the target fragment (return value is false) - > target fragment (return value is true). If there is no fragment on the left or right, it will not be executed.

(2) When a Fragment slides from left to right, first execute setUserVisibleHint (return value is false) for the right Fragment, then setUserVisibleHint (return value is false) for the left Fragment, and finally execute setUserVisibleHint (return value is true) for displaying the Fragment. Slide from right to left. First execute setUserVisibleHint of the left Fragment (return value is false), then setUserVisibleHint of the right Fragment (return value is false), and finally execute setUserVisibleHint of the display Fragment (return value is true). If there is no Fragment on the left or right, it will not be executed.

(3) The instance of Fragment will not be destroyed, the views of fragments around the currently displayed Fragment will not be destroyed, and the views of other non displayed fragments will be destroyed.

(4) When loading data, it can be processed through setUserVisibleHint.

(5) In general, there are three fragments in operation, one in the foreground activity and the left and right two in the background activity.

Use the FragmentStatePagerAdapter method:

Load first page by default

The life cycle of execution is:
(1) Execute setUserVisibleHint of the first Fragment, and the return value is false.
(2) Execute setUserVisibleHint of the second Fragment, and the return value is false.
(3) Execute setUserVisibleHint of the first Fragment, and the return value is true.
(4) Execute onattach - > oncreate of the first Fragment.
(5) Execute onattach - > oncreate of the second Fragment.
(6) Execute oncreateview - > onactivitycreated - > OnStart - > onresume of the first Fragment.
(7) Execute oncreateview - > onactivitycreated - > OnStart - > onresume of the second Fragment.

The second page is loaded by default

The life cycle of execution is:
(1) Execute setUserVisibleHint of the second Fragment, and the return value is false.
(2) Execute setUserVisibleHint of the first Fragment, and the return value is false.
(3) Execute setUserVisibleHint of the third Fragment, and the return value is false.
(4) Execute setUserVisibleHint of the second Fragment, and the return value is true.
(5) Execute onattach - > oncreate of the second Fragment.
(6) Execute onattach - > oncreate of the first Fragment.
(7) Execute onattach - > oncreate of the third Fragment.
(8) Execute oncreateview - > onactivitycreated - > OnStart - > onresume of the second Fragment.
(9) Execute oncreateview - > onactivitycreated of the first Fragment.
(10) Execute oncreateview - > onactivitycreated - > OnStart - > onresume of the third Fragment.
(11) Execute OnStart - > onresume of the first Fragment.

Slide from page 1 to page 2

The life cycle of execution is:
(1) Execute setUserVisibleHint of the third Fragment, and the return value is false.
(2) Execute setUserVisibleHint of the first Fragment, and the return value is false.
(3) Execute setUserVisibleHint of the second Fragment, and the return value is true.
(4) Execute onattach - > oncreate - > oncreateview - > onactivitycreated - > OnStart - > onresume of the third Fragment.

Slide from page 2 to page 3:

The life cycle of execution is:
(1) Execute setUserVisibleHint of the fourth Fragment, and the return value is false.
(2) Execute setUserVisibleHint of the second Fragment, and the return value is false.
(3) Execute setUserVisibleHint of the third Fragment, and the return value is true.
(4) Execute onattach - > oncreate of the fourth Fragment.
(5) Execute onpause - > onstop - > ondestroyview - > ondestroy - > ondetach of the first Fragment.
(6) Execute oncreateview - > onactivitycreated - > OnStart - > onresume of the fourth Fragment.

It should be emphasized that when we create the fourth Fragment, the instance of the first Fragment will be destroyed, while when using the FragmentPagerAdapter, only the view will be destroyed.

Slide from page 3 to page 4:

The life cycle of execution is:
(1) Execute setUserVisibleHint of the third Fragment, and the return value is false.
(2) Execute setUserVisibleHint of the fourth Fragment, and the return value is true.
(3) Execute onpause - > onstop - > ondestroyview - > ondestroy - > ondetach of the second Fragment.

Slide from page 4 to page 3:

The life cycle of execution is:
(1) Execute setUserVisibleHint of the second Fragment, and the return value is false.
(2) Execute setUserVisibleHint of the fourth Fragment, and the return value is false.
(3) Execute setUserVisibleHint of the fourth Fragment, and the return value is true.
(4) Execute onattach - > oncreate - > onactivityed - > OnStart - > onresume of the second Fragment.

To sum up:

(1) When entering for the first time, setUserVisibleHint of the fragment will be called first. The order of execution is target fragment (return value is false) - > adjacent to the left of the target fragment (return value is false) - > adjacent to the right of the target fragment (return value is false) - > target fragment (return value is true). If there is no fragment on the left or right, it will not be executed.

(2) When a Fragment slides from left to right, first execute setUserVisibleHint (return value is false) for the right Fragment, then setUserVisibleHint (return value is false) for the left Fragment, and finally execute setUserVisibleHint (return value is true) for displaying the Fragment. Slide from right to left. First execute setUserVisibleHint of the left Fragment (return value is false), then setUserVisibleHint of the right Fragment (return value is false), and finally execute setUserVisibleHint of the display Fragment (return value is true). If there is no Fragment on the left or right, it will not be executed.

(3) The instances of fragments will be destroyed, the views of fragments around the currently displayed fragments will not be destroyed, and other instances of fragments that are not displayed will be destroyed.

(4) When loading data, it can be processed through setUserVisibleHint.

(5) In general, there are three fragments in operation, one in the foreground and the left and right in the background.

In fact, in the end, we can see that the final difference between using FragmentPagerAdapter and FragmentStatePagerAdapter lies in whether the instance of Fragment is recycled, and other usages are the same. Now we should know how to choose the corresponding method in the daily development process.

Specific description of lazy loading:

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

Where isLoadData is the variable we declare, which represents whether it is loaded for the first time.

Actual combat of Fragment and ViewPager

1. Create layout file

<?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. Write our adapter

/**
 * 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
    }


    /**
     * Number of fragments loaded
     */
    override fun getCount(): Int = mList!!.size


    /**
     * Gets the currently displayed Fragment
     */
    override fun getItem(position: Int): Fragment {
        return mList!![position]
    }

    /**
     * Fragment When removed
     */
    override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
        super.destroyItem(container, position, `object`)
    }

    /**
     * Gets the location of the currently displayed Fragment
     */
    override fun getItemPosition(`object`: Any): Int {
        return `object` as Int
    }

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

3. Direct load Association

val titleList = mutableListOf("first page", "Page 2", "Page 3", "Page 4")
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. Corresponding Fragment

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

/**
 * Blank 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
            // Load data here
        }
    }

    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")
    }
}

Here, a simple Fragment+ViewPager is completed.

Topics: Java Android