ViewPager2 系列-- 初探ViewPager2

ViewPager2是什么

ViewPager2是Android Jetpack库中的一个组件,是用于在应用程序中实现页面切换和滑动效果的容器。

ViewPager2的作用和用途

ViewPager2是一个功能强大的滑动容器,可以应用于多种场景中,提供了灵活的页面切换和布局定制功能,使得应用程序界面更加丰富和交互性强,可以用于以下场景:

  1. 实现引导页或欢迎页:ViewPager2可以用于创建引导页或欢迎页,让用户通过滑动浏览介绍应用程序功能或展示欢迎内容。

  2. 创建图片浏览器:ViewPager2可以用于创建图片浏览器,允许用户通过滑动来切换不同的图片,并支持缩放和手势交互。

  3. 构建轮播图:ViewPager2非常适合构建轮播图功能,可以通过适配器动态加载不同的轮播项,并提供自动循环滚动的功能。

  4. 实现选项卡式布局:结合TabLayout,ViewPager2可以用于创建选项卡式布局,让用户通过滑动选项卡来切换不同的内容页面。

  5. 创建垂直滑动页面:与ViewPager不同,ViewPager2支持垂直方向的滑动,因此可以用于创建垂直滑动的页面布局,例如垂直滑动的导航菜单或垂直的新闻列表。

  6. 实现分页数据展示:ViewPager2可以用于展示分页数据,例如将大量数据按页加载并在每一页中展示一部分内容。

  7. 嵌套滑动布局:ViewPager2可以与其他滑动组件(如RecyclerView)嵌套使用,实现复杂的滑动布局结构。

  8. 实现自定义的滑动效果:通过使用自定义的转换器(Transformer),可以实现各种炫酷的页面切换效果,例如渐变、缩放、旋转等。

ViewPager2相较于ViewPager的改进和优势

ViewPager2是对ViewPager的改进版本,提供了更好的性能、更灵活的适配器和更丰富的功能。它是构建滑动页面布局的首选组件,可以在应用程序中实现各种滑动页面的需求,并提供更好的用户体验,大致有以下几点改进和优势:

  1. 支持垂直滑动:ViewPager2是在ViewPager的基础上进行改进的,最显著的改进之一是支持垂直滑动。而在ViewPager中,只支持水平滑动。这使得ViewPager2在创建垂直布局或特定场景下的垂直滑动功能更加方便和灵活。

  2. 更好的性能和稳定性:ViewPager2内部实现使用了RecyclerView作为容器,而不再依赖于ViewPager的实现方式。这使得ViewPager2具有RecyclerView的优势,例如更好的性能和内存管理、更流畅的滑动体验以及更好的布局回收和复用机制。同时,ViewPager2还解决了ViewPager一些已知的问题和不稳定性,如条目位置错乱、刷新数据的不及时等。

  3. 支持使用Fragment作为页面:与ViewPager不同,ViewPager2直接支持使用Fragment作为页面,而无需通过FragmentPagerAdapter或FragmentStatePagerAdapter进行适配。这简化了页面管理和生命周期处理,并提供了更直观和一致的使用体验。

  4. 更灵活的适配器:ViewPager2引入了新的适配器接口,即RecyclerView.Adapter的子类RecyclerView.Adapter。这使得适配器的创建和管理更加灵活,同时提供了更多的功能和扩展性。

  5. 更丰富的功能和接口:ViewPager2提供了许多新的功能和接口,例如支持页面预加载、更强大的页面切换动画支持、更丰富的回调接口等。这些功能和接口使得开发者能够更好地控制和定制ViewPager2的行为和外观。

环境配置和依赖

  1. 在你的项目模块的build.gradle文件中,添加以下依赖项:
dependencies {
    // ...
    implementation 'androidx.viewpager2:viewpager2:1.0.0'
}

  1. 确保你的项目使用了AndroidX,可以在gradle.properties文件中添加以下配置:
arduinoCopy code
android.useAndroidX=true
android.enableJetifier=true

ViewPager2基本用法

  • 创建一个包含ViewPager2的布局文件

  • 在Activity或Fragment中查找和实例化ViewPager2

  • 创建和设置适配器(Adapter)来管理ViewPager2的内容

  • 设置适配器到ViewPager2实例

与View结合使用

以下是使用Kotlin的ViewPager2基本用法示例:

  1. 在XML布局文件中定义ViewPager2:
<androidx.viewpager2.widget.ViewPager2
    android:id="@+id/viewPager"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

  1. 在Activity或Fragment中,获取ViewPager2实例并设置适配器:
val viewPager: ViewPager2 = findViewById(R.id.viewPager)
val adapter = MyAdapter() // 自定义适配器,需要继承RecyclerView.Adapter<ViewHolder>
viewPager.adapter = adapter

  1. 创建自定义适配器MyAdapter,继承自RecyclerView.Adapter<ViewHolder>
class MyAdapter : RecyclerView.Adapter<MyViewHolder>() {

    // 在这里定义你的数据源
    private val dataList: MutableList<String> = mutableListOf()

    // 添加数据到数据源
    fun setData(data: List<String>) {
        dataList.clear()
        dataList.addAll(data)
        notifyDataSetChanged()
    }

    // 创建ViewHolder
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.item_view, parent, false)
        return MyViewHolder(view)
    }

    // 绑定数据到ViewHolder
    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        val data = dataList[position]
        holder.bindData(data)
    }

    // 返回数据源的大小
    override fun getItemCount(): Int {
        return dataList.size
    }
}

  1. 创建ViewHolder类MyViewHolder,继承自RecyclerView.ViewHolder
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

    private val textView: TextView = itemView.findViewById(R.id.textView)

    fun bindData(data: String) {
        textView.text = data
    }
}

  1. item_view.xml布局文件中定义每个页面的布局,例如一个简单的TextView:
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/itemLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
    android:id="@+id/textView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textSize="16sp"
    android:padding="16dp" />

</LinearLayout>

注意:布局文件的根视图的宽度和高度设置为match_parent ,否则会报错:

java.lang.IllegalStateException: Pages must fill the whole ViewPager2 (use match_parent)

通过上述步骤,就可以在ViewPager2中显示自定义的页面,并通过适配器来管理数据源和页面布局。

与Fragment结合使用

ViewPager2除了配合View使用,更多会和Fragment结合使用,此时我们只需要借助FragmentStateAdapter,简单实现如下:


class MyFragmentStateAdapter(fragmentActivity: FragmentActivity) : FragmentStateAdapter(fragmentActivity) {

    private val fragmentList = listOf(
        FirstFragment(),
        SecondFragment(),
        ThirdFragment()
    )

    override fun getItemCount(): Int {
        return fragmentList.size
    }

    override fun createFragment(position: Int): Fragment {
        return fragmentList[position]
    }
}

然后,在MainActivity中使用ViewPager2和适配器:


class MainActivity : AppCompatActivity() {

    private lateinit var viewPager: ViewPager2

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        viewPager = findViewById(R.id.viewPager)
        val adapter = MyFragmentStateAdapter(this)
        viewPager.adapter = adapter
    }
}

如何监听页面切换事件

要监听页面切换事件,你可以在 ViewPager2 上设置一个 OnPageChangeCallback 对象来监听页面的变化。OnPageChangeCallback 提供了几个方法,可以在页面被选中、滚动和滚动状态改变时触发相应的回调。

下面是一个示例,展示了如何监听页面切换事件:

import androidx.appcompat.app.AppCompatActivity
import androidx.viewpager2.widget.ViewPager2
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback

class MainActivity : AppCompatActivity() {

    private lateinit var viewPager: ViewPager2

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        viewPager = findViewById(R.id.viewPager)

        // 设置页面切换监听
        viewPager.registerOnPageChangeCallback(object : OnPageChangeCallback() {
            override fun onPageSelected(position: Int) {
                // 当页面选中时触发回调
                // 在这里可以根据需要执行相应的操作
            }

            override fun onPageScrolled(
                position: Int,
                positionOffset: Float,
                positionOffsetPixels: Int
            ) {
                // 当页面滚动时触发回调
                // 在这里可以根据需要执行相应的操作
            }

            override fun onPageScrollStateChanged(state: Int) {
                // 当页面滚动状态改变时触发回调
                // 在这里可以根据需要执行相应的操作
            }
        })
    }
}

页面滚动状态都有哪些

ViewPager2 的页面滚动状态有三种,分别对应不同的整数值:

  1. ViewPager2.SCROLL_STATE_IDLE(值为 0):空闲状态。表示当前页面处于静止状态,没有正在进行的滚动操作。

  2. ViewPager2.SCROLL_STATE_DRAGGING(值为 1):拖动状态。表示用户正在拖动页面,准备进行滚动操作。

  3. ViewPager2.SCROLL_STATE_SETTLING(值为 2):滚动状态。表示页面正在自动滚动到最终的位置。

可以通过 ViewPager2.OnPageChangeCallbackonPageScrollStateChanged() 方法中的 state 参数获取当前的页面滚动状态。根据不同的状态值,可以执行相应的操作,例如显示加载指示器、更新界面等。

自定义动画切换动画和过渡效果

要自定义页面切换动画和过渡效果,我们可以使用 ViewPager2.PageTransformer 接口来实现。PageTransformer 允许在页面切换时对页面应用自定义的动画和过渡效果。

下面是一个示例,展示了如何自定义页面切换动画和过渡效果:

import android.view.View
import androidx.viewpager2.widget.ViewPager2

class CustomPageTransformer : ViewPager2.PageTransformer {

    override fun transformPage(page: View, position: Float) {
        val absPosition = Math.abs(position)

        // 在这里根据需要对页面进行自定义动画和过渡效果的操作

        // 例如,可以对页面进行缩放和透明度变化
        page.scaleY = 0.85f + (1f - 0.85f) * (1f - absPosition)
        page.alpha = 0.5f + (1f - 0.5f) * (1f - absPosition)
    }
}

在上述示例中,我们创建了一个名为 CustomPageTransformer 的类,并实现了 ViewPager2.PageTransformer 接口。在 transformPage() 方法中,我们可以根据需要对每个页面进行自定义的动画和过渡效果。

在这个示例中,我们对页面进行了简单的缩放和透明度变化。根据页面的位置(position),我们设置了不同的缩放和透明度值。

接下来,将 CustomPageTransformer 应用到 ViewPager2 上:

val viewPager: ViewPager2 = findViewById(R.id.viewPager)
val adapter = MyAdapter()
viewPager.adapter = adapter

val pageTransformer = CustomPageTransformer()
viewPager.setPageTransformer(pageTransformer)

在这个示例中,我们首先实例化了 ViewPager2 和适配器 MyAdapter。然后,我们创建了 CustomPageTransformer 的实例,并通过 setPageTransformer() 方法将其应用到 ViewPager2 上。

如何禁用或限制页面切换

要禁用或限制页面切换,可以使用 ViewPager2.OnPageChangeCallback 监听器来控制页面切换的行为。通过在回调方法中处理逻辑,你可以决定是否允许页面切换。

下面是一个示例,展示了如何禁用或限制页面切换:

import androidx.viewpager2.widget.ViewPager2

class CustomOnPageChangeCallback : ViewPager2.OnPageChangeCallback() {

    private var isPageChangeEnabled = true

    fun setPageChangeEnabled(enabled: Boolean) {
        isPageChangeEnabled = enabled
    }

    override fun onPageSelected(position: Int) {
        if (!isPageChangeEnabled) {
            // 如果页面切换被禁用,则强制将选中的页面切换回原始位置
            // 这样可以避免用户手动滑动页面
            val viewPager = /* 获取 ViewPager2 实例 */
            viewPager.setCurrentItem(/* 原始位置 */, false)
        }
    }

    override fun onPageScrollStateChanged(state: Int) {
        if (!isPageChangeEnabled && state == ViewPager2.SCROLL_STATE_DRAGGING) {
            // 如果页面切换被禁用,并且用户尝试拖动页面,
            // 则强制将滚动状态设置为 SCROLL_STATE_IDLE,防止页面滚动
            val viewPager = /* 获取 ViewPager2 实例 */
            viewPager.scrollToPosition(/* 原始位置 */)
        }
    }
}

在上述示例中,我们创建了一个名为 CustomOnPageChangeCallback 的类,继承自 ViewPager2.OnPageChangeCallback。我们添加了一个 setPageChangeEnabled() 方法,用于启用或禁用页面切换。

onPageSelected() 方法中,我们检查 isPageChangeEnabled 的状态。如果页面切换被禁用,我们使用 ViewPager2 实例将选中的页面切换回原始位置,这样可以防止用户手动滑动页面。

onPageScrollStateChanged() 方法中,我们检查 isPageChangeEnabled 的状态以及滚动状态。如果页面切换被禁用,并且用户尝试拖动页面,我们强制将滚动状态设置为 SCROLL_STATE_IDLE,这样可以防止页面滚动。

然后,将 CustomOnPageChangeCallback 应用到 ViewPager2 上:

val viewPager: ViewPager2 = findViewById(R.id.viewPager)
val adapter = MyAdapter()
viewPager.adapter = adapter

val onPageChangeCallback = CustomOnPageChangeCallback()
viewPager.registerOnPageChangeCallback(onPageChangeCallback)

在这个示例中,我们首先实例化了 ViewPager2 和适配器 MyAdapter。然后,我们创建了 CustomOnPageChangeCallback 的实例,并通过 registerOnPageChangeCallback() 方法将其注册到 ViewPager2 上。

要禁用或启用页面切换,只需调用 setPageChangeEnabled() 方法并传递相应的参数即可:

onPageChangeCallback.setPageChangeEnabled(false) // 禁用页面切换
onPageChangeCallback.setPageChangeEnabled(true)

滑动方向设置

要设置 ViewPager2 的滑动方向,你可以通过设置 Orientation 属性来实现。ViewPager2 支持两种滑动方向:水平滑动和垂直滑动。

在布局文件中,将 ViewPager2android:orientation 属性设置为 horizontal(水平滑动)或 vertical(垂直滑动)即可。

<androidx.viewpager2.widget.ViewPager2
    android:id="@+id/viewPager"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" />

在上述示例中,我们将 ViewPager2android:orientation 属性设置为 "horizontal",以实现水平滑动。如果你想实现垂直滑动,只需将属性值设置为 "vertical"

页面预加载

要设置 ViewPager2 的页面预加载数量,你可以使用 setOffscreenPageLimit() 方法。setOffscreenPageLimit() 方法用于设置 ViewPager2 在当前页面附近预加载的页面数量。

默认情况下,ViewPager2 的页面预加载数量为 1,即当前页面的左右各一个页面会被预加载。你可以根据需要增加或减少预加载的页面数量。

以下是示例代码,展示了如何设置 ViewPager2 的页面预加载数量为 2:

val viewPager: ViewPager2 = findViewById(R.id.viewPager)
viewPager.offscreenPageLimit = 2

在上述示例中,我们通过 viewPager.offscreenPageLimit 属性将页面预加载数量设置为 2。这意味着在当前页面的左右各两个页面会被预加载。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容