今天看到美颜相机里的首页广告指示器有点好玩,就想着自己写一个
现在开始进行分析,首先需要测量指示器的宽高,大致分为这几个部分
所以我们重写onMeasured的时候只需要根据 间隔 space,R小圆圈半径,滑块长度
测量好后我们进行具体的绘制,小圆圈我们直接用 canvas.drawCircle,滑块直接使用一条横线就行,横线的strokeWidth边缘必须是
小圆圈的直径。
viewPager 在当前页的时候当前小圆圈和下一个小圆圈之间会多出barWidth - 2*R的空间用来填充滑块
所以他的画法是
先画第一个圆圈,如果是显示当前下标再画滑块 然后画布镜头右移 barWidth + space - 2R(这里减2R是因为画滑块的画笔是 strokeWidth =2R,
strokeCap=Paint.Cap.ROUND的)
最后贴出效果
全部代码如下(没做相关封装,只是实现)
import android.animation.ValueAnimator import android.content.Context import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint import android.graphics.RectF import android.graphics.drawable.Drawable import android.os.Build import android.util.AttributeSet import android.view.View import android.view.ViewGroup import android.view.animation.AccelerateDecelerateInterpolator import android.view.animation.LinearInterpolator import android.widget.FrameLayout import android.widget.Scroller import androidx.annotation.RequiresApi import androidx.viewpager.widget.ViewPager import androidx.viewpager2.widget.ViewPager2 import com.lu.demoy.widget.i.YIndicatorAdapter import java.lang.Exception class YIndicator @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : View(context, attrs, defStyleAttr) { init { initProperty() } override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { circleW = radius * 2 radiusDistanceBar = (scrollBarWidth-circleW).coerceAtLeast(0f) val w = (childAmount -1)*spacing + circleW*childAmount + radiusDistanceBar setMeasuredDimension(w.toInt(),circleW.toInt()) } private var radiusDistanceBar = 0f private var childAmount = 0 private var spacing = 0f private var scrollBarWidth = 0f private var radius = 0f private var circleW = 0f private var currentIndex = 1 private var currentOffset = 0f private var crossDistance = 0f private var valueAnimator:ValueAnimator?=null private var onPagerChangeListener:ViewPager2.OnPageChangeCallback?=null private var pagerViewPager2:ViewPager2? = null private var timeInterceptor = 0L fun setBarWidth(setScrollBarWidth:Float):YIndicator{ this.scrollBarWidth = setScrollBarWidth return this } fun setRadius(radius:Float):YIndicator{ this.radius = radius paintBar.strokeWidth=radius*2 return this } fun setSpacing(spacing:Float):YIndicator{ this.spacing = spacing return this } fun setPager(viewPager: ViewPager2){ if (viewPager.adapter !is YIndicatorAdapter<*>) { throw Exception("adapter must be YIndicatorAdapter") } pagerViewPager2 = viewPager childAmount = (viewPager.adapter as YIndicatorAdapter<*>).getViewSize() onPagerChangeListener?.let { pagerViewPager2?.unregisterOnPageChangeCallback(it) } onPagerChangeListener =object:ViewPager2.OnPageChangeCallback(){ override fun onPageScrolled( position: Int, positionOffset: Float, positionOffsetPixels: Int) { if(System.currentTimeMillis() - timeInterceptor>100){ timeInterceptor = System.currentTimeMillis() currentIndex = position % childAmount +1 val crossAll = (currentIndex == childAmount) && (positionOffset > 0) ||(currentIndex == 1) && (positionOffset < 0) crossDistance = (if (crossAll)(-spacing*(childAmount-1)) else spacing) currentOffset =crossDistance * positionOffset invalidate() } if (positionOffset == 0.0f){ currentIndex = position % childAmount +1 currentOffset = 0f println("当前页:$currentIndex,positionOffset:$positionOffset") invalidate() } } } viewPager.registerOnPageChangeCallback(onPagerChangeListener!!) if(childAmount!=0){ requestLayout() } } private fun release(){ onPagerChangeListener?.let { pagerViewPager2?.unregisterOnPageChangeCallback(it) } pagerViewPager2 = null onPagerChangeListener = null valueAnimator?.removeAllUpdateListeners() valueAnimator?.cancel() valueAnimator = null } override fun onDetachedFromWindow() { super.onDetachedFromWindow() release() super.onDetachedFromWindow() } lateinit var paintIndicator:Paint lateinit var paintBar:Paint private fun initProperty(){ paintIndicator= Paint() paintBar = Paint() paintIndicator.color = Color.WHITE paintIndicator.isAntiAlias = true paintBar.color = Color.WHITE paintBar.isAntiAlias = true paintBar.strokeCap=Paint.Cap.ROUND } override fun onDraw(canvas: Canvas?) { canvas?.save() for (a in 1..childAmount){ canvas?.drawCircle(radius,radius,radius,paintIndicator) if (a == currentIndex){ //画滚动条 canvas?.drawLine(radius+currentOffset,radius,scrollBarWidth-radius+currentOffset,radius,paintBar) } if(a == currentIndex){ canvas?.translate(radiusDistanceBar,0f) } canvas?.translate(spacing+radius,0f) } canvas?.restore() } }
abstract class YIndicatorAdapter<T: RecyclerView.ViewHolder>:
RecyclerView.Adapter<T>() {
abstract fun getViewSize():Int
}