文章目录
[一、ViewPager 和 ViewPager2](#一、ViewPager 和 ViewPager2)
二、ViewPager2常用API
[2.1 FragmentStateAdapter](#2.1 FragmentStateAdapter)
[2.2 setCurrentItem](#2.2 setCurrentItem)
[2.3 setPageTransformer](#2.3 setPageTransformer)
[2.4 setOffscreenPageLimit](#2.4 setOffscreenPageLimit)
[2.5 OnPageChangeCallback](#2.5 OnPageChangeCallback)
三、Fragment作为page时,预加载页面的生命周期
[3.1 ViewPager2的页面生命周期](#3.1 ViewPager2的页面生命周期)
[3.2 ViewPager的页面生命周期](#3.2 ViewPager的页面生命周期)
[四、简易的ViewPager2 + Fragment](#四、简易的ViewPager2 + Fragment)
[4.1 Activity实现](#4.1 Activity实现)
[4.2 Adapter实现](#4.2 Adapter实现)
ViewPager2基于RecycleView实现,性能更好
ViewPager2支持水平和垂直方向滑动。ViewPager默认只支持水平滑动,如果要做垂直滑动,要做很多的定制操作,比较麻烦。
特性
ViewPager
ViewPager2
底层实现
基于 AdapterView(类似 ListView)
基于 RecyclerView
方向支持
仅水平方向
水平 + 垂直方向
Adapter
PagerAdapter
RecyclerView.Adapter
Fragment支持
FragmentPagerAdapter / FragmentStatePagerAdapter
FragmentStateAdapter
更新方式
notifyDataSetChanged()
notifyItemChanged() / DiffUtil
性能
较低
高(复用机制好)
2.1 FragmentStateAdapter
Fragment作为页面元素时使用的Adapter
2.2 setCurrentItem
设置当前的页面,可以配置smoothScroll
结合handler 定时任务,可以实现页面的轮播。在页面轮播时需要注意用户手动滑动时,需要取消轮播定时器。
ViewPager2自动轮播时,平台并没有提供接口用于设置两个Page切换动画的时长,比如想让page间切换的速度慢一些。实现起来比较繁琐
2.3 setPageTransformer
自定义页面切换的动效,主要是手动滑动时的动效。自动轮播不会使用这个动效。
2.4 setOffscreenPageLimit
设置ViewPager2的预加载页面数量,左右各有几个页面预加载或者保存在内存中。至少是1个页面进行预加载。
预加载的页面过多,会导致内存增加。预加载的页面少,流畅性就会降低,需要平衡。
2.5 OnPageChangeCallback
onPageScrollStateChanged滑动状态回调。当手指滑动的时候,首先回调的是SCROLL_STATE_DRAGGING,手指抬起后回调SCROLL_STATE_SETTLING,等到页面完全静止后回调SCROLL_STATE_IDLE。
回调状态字段
状态
数值
SCROLL_STATE_IDLE
滑动完全停止
0
SCROLL_STATE_DRAGGING
手指滑动状态,手没有抬起
1
SCROLL_STATE_SETTLING
沉降状态,手指抬起了,自动滑动状态
2
onPageScrolled滑动的位置和Position回调
onPageSelected回调当前选中的页面
一次手指滑动翻页的回调顺序如下,需要注意onPageSelected回调之后,页面仍然时再滑动的。
java
复制代码
I/ViewPager2Activity: -------- onPageScrollStateChanged: state 1 ----------
I/ViewPager2Activity: onPageScrolled: position: 1, positionOffset: 0.014814815, 16
I/ViewPager2Activity: onPageScrolled: position: 1, positionOffset: 0.06759259, 73
I/ViewPager2Activity: onPageScrolled: position: 1, positionOffset: 0.2574074, 278
I/ViewPager2Activity: onPageScrolled: position: 1, positionOffset: 0.27222222, 294
I/ViewPager2Activity: -------- onPageScrollStateChanged: state 2 ----------
I/ViewPager2Activity: -------- onPageSelected: position 2 -------------
I/ViewPager2Activity: onPageScrolled: position: 1, positionOffset: 0.34814814, 376
I/ViewPager2Activity: onPageScrolled: position: 1, positionOffset: 0.42407408, 458
I/ViewPager2Activity: onPageScrolled: position: 1, positionOffset: 0.49166667, 531
I/ViewPager2Activity: onPageScrolled: position: 1, positionOffset: 0.55925924, 604
I/ViewPager2Activity: onPageScrolled: position: 1, positionOffset: 0.6175926, 667
I/ViewPager2Activity: onPageScrolled: position: 1, positionOffset: 0.675, 729
I/ViewPager2Activity: onPageScrolled: position: 1, positionOffset: 0.7259259, 784
I/ViewPager2Activity: --------- onPageScrollStateChanged: state 0 ----------
三、Fragment作为page时,预加载页面的生命周期
只有当前可见Fragment页面会执行onResume()。预加载页面的生命周期会执行到onStart(),不会执行onResume()。当页面划走不可见后,页面也会执行onPause()。
VIewPager默认情况下当前页面和预加载的页面都会进入onResume()状态。
造成的问题:Fragment内无法知道当前正在展示的页面,因为当前页面和预加载的页面都会执行到onResume()状态。如果某些操作在Fragment页面真正展示的时候才执行,而在预加载阶段不执行,那么就需要获取当前正在展示的页面。
这种情况只能在外部把VIewPager当前页面index传入到Fragment内部,增加了复杂性。也造成了一些耦合。
Android平台配置:FragmentPagerAdapter的构造参数可以设置Fragment在预载时的生命周期,通过设置BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT,预载阶段的Fragment就只会执行到onStart状态,不会执行onResume状态。
java
复制代码
/**
* 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;
4.1 Activity实现
kotlin
复制代码
package com.example.myapplication2
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.Trace
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.viewpager2.widget.ViewPager2
import kotlinx.coroutines.Runnable
import kotlin.math.abs
class ViewPager2Activity : AppCompatActivity() {
companion object {
const val TAG = "ViewPager2Activity"
}
private lateinit var viewPager: ViewPager2
private val onPageChangeCallback = object : ViewPager2.OnPageChangeCallback() {
override fun onPageScrollStateChanged(state: Int) {
Log.i(TAG, "--------- onPageScrollStateChanged: $state ----------")
Trace.beginSection("onPageScrollStateChanged: $state")
super.onPageScrollStateChanged(state)
Trace.endSection()
}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
Log.i(TAG, "onPageScrolled: position: $position, positionOffset: $positionOffset, $positionOffsetPixels")
Trace.beginSection("onPageScrolled: $position")
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
Trace.endSection()
}
override fun onPageSelected(position: Int) {
Log.i(TAG, "-------- onPageSelected: $position -------------")
Trace.beginSection("onPageSelected $position")
super.onPageSelected(position)
// 如果滑动到最后一个页面,则跳转到第一个页面。
if (position == viewPager.adapter?.itemCount?.minus(1)) {
viewPager.setCurrentItem(0, false)
}
Trace.endSection()
}
}
private lateinit var handler: Handler
private val nextPageRunnable = object : Runnable {
override fun run() {
viewPager.setCurrentItem(viewPager.currentItem + 1, true)
handler.postDelayed(this, 3000)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.view_pager2_activity)
viewPager = findViewById(R.id.view_pager2)
viewPager.adapter = ViewPager2Adapter(this)
viewPager.registerOnPageChangeCallback(onPageChangeCallback)
viewPager.setPageTransformer { page, position ->
// 设置页面变换效果
val scale = 1 - abs(position) * 0.3f
page.scaleX = scale
page.scaleY = scale
page.alpha = 0.5f + (1 - abs(position)) * 0.5f
}
Log.i("MainActivity", "onCreate: ${viewPager.offscreenPageLimit}")
// 设置ViewPager2的预加载页面数量,左右各有2个页面预加载或者保存在内存中
viewPager.offscreenPageLimit = 1
// 每一秒切换一个页面
handler = Handler(Looper.getMainLooper())
handler.postDelayed(nextPageRunnable, 3000)
}
}
4.2 Adapter实现
kotlin
复制代码
package com.example.myapplication2
import android.util.Log
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.example.myapplication2.ui.dashboard.DashboardFragment
import com.example.myapplication2.ui.home.HomeFragment
import com.example.myapplication2.ui.notifications.NotificationsFragment
class ViewPager2Adapter(activity: FragmentActivity): FragmentStateAdapter(activity) {
companion object {
private const val TAG = "ViewPager2Adapter"
}
override fun getItemCount(): Int {
return 8
}
override fun createFragment(position: Int): Fragment {
Log.i(TAG, "----------createFragment: $position -----------------")
return when (position % 3) {
0 -> HomeFragment(position)
1 -> DashboardFragment(position)
2 -> NotificationsFragment(position)
else -> throw IllegalStateException("Invalid position: $position")
}
}
}