Android中为RecyclerView实现Banner轮播式CardView动画效果
在RecyclerView中实现CardView轮播动画方案
核心思路
通过自定义RecyclerView.LayoutManager控制CardView的排列规则(居中放大、两侧层叠缩小),结合定时任务实现自动轮播,同时在滑动过程中动态调整卡片的缩放与偏移量,达成Banner式的过渡动画效果。
具体实现步骤
1. 自定义LayoutManager控制卡片布局
重写LayoutManager的核心方法,实现卡片的层叠排列与缩放逻辑:
class BannerLayoutManager : RecyclerView.LayoutManager() { private val MAX_SCALE = 1.0f private val MIN_SCALE = 0.8f private val HORIZONTAL_OFFSET = 50 // 两侧卡片的水平偏移像素值 override fun generateDefaultLayoutParams(): RecyclerView.LayoutParams { return RecyclerView.LayoutParams( RecyclerView.LayoutParams.WRAP_CONTENT, RecyclerView.LayoutParams.WRAP_CONTENT ) } override fun onLayoutChildren(recycler: RecyclerView.Recycler, state: RecyclerView.State) { if (itemCount == 0) { detachAndScrapAttachedViews(recycler) return } detachAndScrapAttachedViews(recycler) // 布局所有可见卡片 for (i in 0 until itemCount) { val view = recycler.getViewForPosition(i) addView(view) measureChildWithMargins(view, 0, 0) val centerX = width / 2 val viewWidth = getDecoratedMeasuredWidth(view) val viewHeight = getDecoratedMeasuredHeight(view) // 计算卡片位置:中间卡片居中,两侧卡片向左右偏移 val left = centerX - viewWidth / 2 + (i - findCenterPosition()) * HORIZONTAL_OFFSET val top = (height - viewHeight) / 2 // 根据位置设置缩放比例 val scale = if (i == findCenterPosition()) MAX_SCALE else MIN_SCALE view.scaleX = scale view.scaleY = scale layoutDecorated(view, left, top, left + viewWidth, top + viewHeight) } } private fun findCenterPosition(): Int { return itemCount / 2 } override fun canScrollHorizontally(): Boolean = true override fun scrollHorizontallyBy(dx: Int, recycler: RecyclerView.Recycler, state: RecyclerView.State): Int { // 滑动时实时调整卡片的位置与缩放 offsetChildrenHorizontal(-dx) updateChildScaleAndPosition() return dx } private fun updateChildScaleAndPosition() { val centerX = width / 2 for (i in 0 until childCount) { val view = getChildAt(i) ?: continue val viewCenterX = getDecoratedLeft(view) + getDecoratedMeasuredWidth(view) / 2 val distance = Math.abs(centerX - viewCenterX) // 根据距离中心的远近距离动态计算缩放比例 val scale = MAX_SCALE - (distance / (width / 2)) * (MAX_SCALE - MIN_SCALE) view.scaleX = scale.coerceAtLeast(MIN_SCALE) view.scaleY = scale.coerceAtLeast(MIN_SCALE) } } }
2. 实现自动轮播逻辑
使用Handler或协程实现定时滚动,同时处理手动滑动时的暂停/恢复:
private val handler = Handler(Looper.getMainLooper()) private val carouselRunnable = object : Runnable { override fun run() { val currentPos = recyclerView.layoutManager?.findFirstVisibleItemPosition() ?: 0 val nextPos = (currentPos + 1) % adapter.itemCount recyclerView.smoothScrollToPosition(nextPos) handler.postDelayed(this, 3000) // 3秒自动切换一次 } } // 页面可见时启动轮播 override fun onResume() { super.onResume() handler.postDelayed(carouselRunnable, 3000) } // 页面不可见时停止轮播 override fun onPause() { super.onPause() handler.removeCallbacks(carouselRunnable) } // 监听RecyclerView触摸事件,手动滑动时暂停轮播 recyclerView.addOnItemTouchListener(object : RecyclerView.SimpleOnItemTouchListener() { override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean { if (e.action == MotionEvent.ACTION_DOWN) { handler.removeCallbacks(carouselRunnable) } else if (e.action == MotionEvent.ACTION_UP) { handler.postDelayed(carouselRunnable, 3000) } return super.onInterceptTouchEvent(rv, e) } })
3. 无限轮播优化
让Adapter支持无限循环,避免轮播到末尾时的跳转突兀:
class BannerAdapter(private val realData: List<CardData>) : RecyclerView.Adapter<BannerViewHolder>() { override fun getItemCount(): Int { return Int.MAX_VALUE // 返回极大值模拟无限循环 } override fun onBindViewHolder(holder: BannerViewHolder, position: Int) { val realPos = position % realData.size val data = realData[realPos] // 绑定卡片数据 } }
内容的提问来源于stack exchange,提问作者Rohit Kumar Sehrawat
相关产品推荐
相关产品推荐

