Android基于ViewPager2实现循环轮播图、3D画廊效果

一、前言

在开发中经常会遇到循环轮播图,之前的实现方式是在Activity中使用定时器控制轮播。后面想了想,看能不能把ViewPager2和定时器封装成自定义控件方便移植。然后就有了这个自定义控件。

二、控件实现

因为使用的是ViewPager2,所以要用到适配器,这里写了个BaseBannerAdapter用来设置循环滚动

/**
 * @ Author : 廖健鹏
 * @ Date : 2021/7/27
 * @ e-mail : [email protected]
 * @ Description :[BannerViewPager] 轮播控件所需的 BaseAdapter
 */
abstract class BaseBannerAdapter<T, VH : RecyclerView.ViewHolder> : RecyclerView.Adapter<VH>() {
   
    protected var mList: MutableList<T> = mutableListOf()
    var isCanLoop = false
    var pageClickListener: OnPageClickListener? = null

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
   
        val inflate =
            LayoutInflater.from(parent.context).inflate(getLayoutId(viewType), parent, false)
        return createViewHolder(parent, inflate, viewType)
    }

    override fun onBindViewHolder(holder: VH, position: Int) {
   
        val realPosition: Int = getRealPosition(position)
        holder.itemView.setOnClickListener {
   
            pageClickListener?.onPageClick(realPosition)
        }
        onBind(holder, mList[realPosition], realPosition, mList.size)
    }

    override fun getItemCount(): Int {
   
        return if (isCanLoop && mList.size > 1) {
   
            Int.MAX_VALUE
        } else {
   
            mList.size
        }
    }

    fun getData(): List<T> {
   
        return mList
    }

    fun setData(list: List<T>) {
   
        mList.clear()
        mList.addAll(list)
    }

    fun getListSize(): Int {
   
        return mList.size
    }

    fun getRealPosition(position: Int): Int {
   
        val pageSize = mList.size
        if (pageSize == 0) {
   
            return 0
        }
        return if (isCanLoop) (position + pageSize) % pageSize else position
    }

    interface OnPageClickListener {
   
        fun onPageClick(position: Int)
    }


    protected abstract fun onBind(holder: VH, data: T, position: Int, pageSize: Int)
    abstract fun createViewHolder(parent: ViewGroup, itemView: View, viewType: Int): VH
    abstract fun getLayoutId(viewType: Int): Int

}

BannerViewPager.kt

/**
 * @ Author : 廖健鹏
 * @ Date : 2021/7/27
 * @ e-mail : [email protected]
 * @ Description : 自定义轮播图
 */

class BannerViewPager<T> : RelativeLayout, LifecycleObserver {
   

    private var mContext: Context = context
    private lateinit var mIndicatorLayout: LinearLayout
    private lateinit var mViewPager: ViewPager2
    private var mBannerPagerAdapter: BaseBannerAdapter<T, *>? = null
    private val onPageChangeCallback: OnPageChangeCallback? = null
    private var mCompositePageTransformer: CompositePageTransformer? = null
    private var mMarginPageTransformer: MarginPageTransformer? = null
    private var mOnPageClickListener: BaseBannerAdapter.OnPageClickListener? = null

    //记录轮播图最后所在的位置
    private var lastPosition = 0
    private var listSize = 0


    /**
     * 轮播定时器
     */
    private val mHandler: Handler = Handler()
    private val runnable: Runnable = object : Runnable {
   
        override fun run() {
   
            val currentItem = mViewPager.currentItem
            if (isLooper) {
   
                mViewPager.currentItem = currentItem + 1
            } else {
   
                if (currentItem == listSize - 1) {
   
                    mViewPager.setCurrentItem(0, false)
                } else {
   
                    mViewPager.currentItem = currentItem + 1
                }
            }
            mHandler.postDelayed(this, interval)
        }
    }

    /**
     * 轮播间隔时间
     */
    private var interval = 3000L

    /**
     * 是否自动轮播
     */
    private var isAutoPlay = false

    /**
     * 是否循环
     */
    private var isLooper = false

    /**
     * 是否显示指示器
     */
    private var isShowIndicator = false

    /**
     * 页边距
     */
    private var pageMargin = 0

    /**
     * 一屏多页模式下两边页面显露出来的宽度
     */
    private var revealWidth = -1


    /**
     * 当前可见的任一侧的页数
     */
    private var offscreenPageLimit = 3

    /**
     * 指示器间隔
     */
    private var indicatorMargin = dpToPx(5)

    /**
     * 正常指示器图片
     */
    private var normalImage = R.drawable.shape_dot

    /**
     * 选中指示器图片
     */
    private var checkedImage = R.drawable.shape_dot_selected


    private val mOnPageChangeCallback: OnPageChangeCallback = object : OnPageChangeCallback() {
   
        override fun onPageScrolled(
            position: Int,
            positionOffset: Float,
            positionOffsetPixels: Int
        ) {
   
            super.onPageScrolled(position, positionOffset
  • 5
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值