现在很多app除了banner以外,集中的功能展示区都会有这种需要展示多个,甚至翻页的情况,通常的设计有两种, 一种是类似于banner的翻页的,比如京东app;另一种是平滑的滚动,比如淘宝app。
下面我我们实际看一下这个效果
淘宝首页
京东到家首页
下面使我们自定义的效果
其实这个很简单,主要就是有以下几点
- 绘制一个圆角矩形做背景;
- 绘制一个圆角矩形做指示器;
- 确定指示器的长度和指示器的位置;
- 根据RecyclerView滑动的距离动态改变指示器的位置。
绘制背景的圆角矩形的时候,不考虑padding信息,就很简单
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
viewWidth = w
mBgRect.set(0f, 0f, w * 1f, h * 1f)
mRadius = h / 2f
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
//绘制背景
canvas?.drawRoundRect(mBgRect, mRadius, mRadius, mBgPaint)
}
绘制指示器
ratio指的是指示器长度,即如果滚动内容有两屏,则指示器应该为1/2长度,以此类推 (当然上面所示app不一定实现了这个,可能会为了美观设置一个固定比例)
progress指的是滑动距离和指示器对应关系,这个实际上就是滑动进度条的意思
//计算指示器的长度和位置
val leftOffset = viewWidth * (1f - ratio) * progress
val left = mBgRect.left + leftOffset
val right = left + viewWidth * ratio
mRect.set(left, mBgRect.top, right, mBgRect.bottom)
//绘制指示器
canvas?.drawRoundRect(mRect, mRadius, mRadius, mPaint)
获取RecyclerView滚动的位置可根据以下几个方法获取
- computeVerticalScrollExtent()/computeHorizontalScrollExtent是当前屏幕显示的区域高度
- computeVerticalScrollOffset()/computeHorizontalScrollOffset 是当前屏幕之前滑过的距离
- computeVerticalScrollRange()/computeHorizontalScrollRange是整个RecycleView控件的高度
监听滑动,配合上诉方法就可以拿到滑动位置的比例
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val offsetX = recyclerView.computeHorizontalScrollOffset()
val range = recyclerView.computeHorizontalScrollRange()
val extend = recyclerView.computeHorizontalScrollExtent()
val progress: Float = offsetX * 1.0f / (range - extend) //因为指示器有长度,所以这里需要减去首屏长度
this@HIndicator.progress = progress //设置滚动距离所占比例
}
})