一步一步,自定义viewPager指示器

今天看到美颜相机里的首页广告指示器有点好玩,就想着自己写一个

现在开始进行分析,首先需要测量指示器的宽高,大致分为这几个部分

 

 

 所以我们重写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
}
 

 

posted on 2021-04-02 11:54  老公公-Q  阅读(468)  评论(0编辑  收藏  举报

导航