ViewPager2实现循环画廊

本文介绍了如何在Android中利用ViewPager2实现循环画廊效果,包括布局设置、适配器编写、页面切换动画以及Activity中的相关配置,重点展示了如何处理首尾空白页以达到无缝循环滑动。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


前言

由于想要更加好看的效果所以在网上学习了ViewPager2实现循环画廊的代码,在这分享一下学习心得。


一、ViewPager2

ViewPager与ViewPager2

参考:ViewPager2与ViewPager的区别

ViewPager2是一个非常优秀的框架,它继承自RecyclerView,因此拥有良好的性能的同时可以实现垂直和水平两种方向的滑动,而ViewPager只能水平滑动;另外,ViewPager2模式实现了懒加载,默认不进行预加载,而ViewPager会进行预加载。需要注意的是,ViewPager 和 TabLayout 的代码关联用的是 TabLayout 的方法 setupWithViewPager();而 ViewPager2 是通过 TabLayoutMediator 类来做关联。

二、实现循环画廊

参考文章:viewpage2 实现中间大两边小

1.布局文件

随便设了一个绿色框,然后在下面设了一个数字为每个item作区分:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/img"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="@color/teal_200" />

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="1"
        android:textSize="@dimen/sp_20"
        android:textColor="@color/black"/>
</LinearLayout>

非常简单的activity。
需要注意的是需要加上android:clipChildren=“false”,并且ViewPager2的大小需要小于父组件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    android:clipChildren="false"
    android:orientation="vertical">

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/vp"
        android:layout_width="match_parent"
        android:layout_height="@dimen/dp_400"
        android:layout_margin="@dimen/dp_50"
        android:clipChildren="false" />

</LinearLayout>

2.适配器

这里的循环其实就是要实现第一个item左滑跳到最后一个item,最后一个item右滑要跳到第一个item,这很容易实现,但由于第一个item前面没有item、最后一个item后面也没有item,所以是空白的,效果上并不好看,因此应该在第一个item前面加上与最后一个item相同的item、最后一个item后面加上与第一个item相同的item,这样滑动的时候肉眼看上去就是循环的效果了。

class GalleryAdapter(val context: Context) : RecyclerView.Adapter<GalleryAdapter.ViewHolder>() {

    private val index = listOf<String>(
            "3", "1", "2", "3", "1" // 关键在于,第一个需要等于倒数第二个,倒数第一个需要等于第二个
    )

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


    class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val tv = itemView.findViewById<TextView>(R.id.tv)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.tv.text = index[position]
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return ViewHolder(LayoutInflater.from(context).inflate(R.layout.item_gallery, parent, false))
    }

}

3.页面切换动画

default:左右page的缩放倍率
default_max:位于中间的page的缩放倍率
default_alpha:左右page的透明度

class GalleryTransform(val viewpae2: ViewPager2) : ViewPager2.PageTransformer {
    private val default = 19 / 20f
    private val default_max = 21 / 20f
    private val TEN = 10
    private val default_alpha = 1 / 2f

    override fun transformPage(page: View, position: Float) {
        // MarginPageTransformer的源码
        val offset: Float = 40 * position
        if (viewpae2.getOrientation() == ViewPager2.ORIENTATION_HORIZONTAL) {
            page.translationX = if (isRtl()) -offset else offset
        } else {
            page.translationY = offset
        }
        // 左滑:左page-1->-2,中page0->-1,右page1->0
        // 右滑:左page-1->0,中page0->1,右page1->2
        when {
            position <= -1 -> {
                page.scaleX = default
                page.scaleY = default
                page.alpha = default_alpha
                Log.e("position", "<-1:$position")
            }

            position <= 0 -> {
                page.scaleX = default_max + position / TEN
                page.scaleY = default_max + position / TEN
                page.alpha = 1 + position * default_alpha
                Log.e("position", "<0:$position")
            }

            position <= 1 -> {
                page.scaleX = default_max - position / TEN
                page.scaleY = default_max - position / TEN
                page.alpha = 1 - position * default_alpha
                Log.e("position", "<1:$position")
            }

            else -> {
                page.scaleX = default
                page.scaleY = default
                page.alpha = default_alpha
                Log.e("position", ">1:$position")
            }
        }

    }

    /**
     * 是否是从右往左滑
     */
    private fun isRtl(): Boolean {
        return viewpae2.layoutDirection == View.LAYOUT_DIRECTION_RTL
    }
}

4.Activity

class GalleryActivity : FragmentActivity() {

    private lateinit var viewPage: ViewPager2

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_gallery)
        viewPage = findViewById(R.id.vp)
        val adapter = GalleryAdapter(this)
        viewPage.adapter = adapter
        viewPage.clipChildren = false;
        viewPage.offscreenPageLimit = 3
        viewPage.currentItem = 1 // 注意初始化时需要跳到第二个item
        viewPage.setPageTransformer(GalleryTransform(viewPage))
        //viewPage.setPageTransformer(ZoomOutPageTransformer())
        viewPage.registerOnPageChangeCallback(object : OnPageChangeCallback() {
            override fun onPageScrollStateChanged(state: Int) {
                super.onPageScrollStateChanged(state)
                if (state == ViewPager2.SCROLL_STATE_IDLE) {
                    if (viewPage.currentItem == 0) {
                        // 滑动到第一页的时候直接跳到倒数第二页,因为两页内容完全一致,跳的过程不会被人眼捕捉到
                        viewPage.setCurrentItem(adapter.itemCount - 2, false)
                    }
                    if (viewPage.currentItem == adapter.itemCount - 1) {
                        // 同理滑动到倒数第一页的时候直接跳到第二页
                        viewPage.setCurrentItem(1, false)
                    }
                }
            }
        })
    }
}

其中ZoomOutPageTransformer是来自开源库,不想要自定义切换动画的也可以用这个代替:

    implementation 'io.github.youth5201314:banner:2.2.2'
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值