ViewPager2实现循环画廊


前言

由于想要更加好看的效果所以在网上学习了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'
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
实现一个ViewPager2画廊,首先需要在布局文件中添加一个ViewPager2,然后创建一个适配器来提供数据和视图。以下是一些代码片段,可以帮助你开始创建一个基本的ViewPager2画廊: 1. 添加ViewPager2到布局文件中: ```xml <androidx.viewpager2.widget.ViewPager2 android:id="@+id/view_pager" android:layout_width="match_parent" android:layout_height="match_parent" /> ``` 2. 创建一个适配器来提供数据和视图: ```kotlin class GalleryAdapter(private val images: List<Int>) : RecyclerView.Adapter<GalleryAdapter.ViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.item_gallery, parent, false) return ViewHolder(view) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { holder.bind(images[position]) } override fun getItemCount(): Int = images.size inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { private val imageView: ImageView = itemView.findViewById(R.id.image_view) fun bind(imageResId: Int) { imageView.setImageResource(imageResId) } } } ``` 3. 设置ViewPager2的适配器: ```kotlin val images = listOf(R.drawable.image1, R.drawable.image2, R.drawable.image3) val galleryAdapter = GalleryAdapter(images) viewPager.adapter = galleryAdapter ``` 4. 设置ViewPager2的滑动方向和页面间距: ```kotlin viewPager.orientation = ViewPager2.ORIENTATION_HORIZONTAL viewPager.offscreenPageLimit = 3 viewPager.setPageTransformer { page, position -> val offset = resources.getDimensionPixelOffset(R.dimen.page_margin) when { position < -1 -> { page.translationX = -(2 * offset) } position <= 1 -> { val scaleFactor = Math.max(0.7f, 1 - Math.abs(position - 0.14285715f)) page.scaleX = scaleFactor page.scaleY = scaleFactor } else -> { page.translationX = (2 * offset) } } } ``` 5. 创建一个布局文件来显示图像: ```xml <ImageView android:id="@+id/image_view" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" /> ``` 以上是一个基本的ViewPager2画廊实现。您可以根据自己的需求进行更改和调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值