双向水平滑动条

2019-12-16  本文已影响0人  案玉璃青

class DoubleSeekBarView :View {

    constructor(ctx:Context) :this(

        ctx, ContextCompat.getDrawable(ctx, R.drawable.seek_bar_thumb)!!.toBitmap(), { _, _,         _ -> }

    )

    constructor(ctx:Context, attrs:AttributeSet?) :super(ctx, attrs, 0) {

        this.sliderBitmap= ContextCompat.getDrawable(ctx,                 R.drawable.seek_bar_thumb)!!.toBitmap()

                this.onSliderListener= { _, _, _ -> }

    }

    constructor(

        ctx:Context,

        sliderBitmap:Bitmap,

        onSliderListener:(slidingDir:Int, currentLeft:Float, currentRight:Float) -> Unit = { _, _, _         -> }

    ) :super(ctx) {

        this.sliderBitmap= sliderBitmap

        this.onSliderListener= onSliderListener

    }

    companion object {

        const val LEFT= 1

        const val RIGHT= 2

    }

    var onSliderListener:(slidingDir:Int, currentLeft:Float, currentRight:Float) -> Unit

    var currentLeft= 0f

    var currentRight= 1f

    private var lineWidth= 0 // 能滑动的宽度,即线宽

    var lineHeight= dip(5)

    private var slidingDir= LEFT // 滑动方向

    private var sliderWidth= 0 // 滑块宽度,即 sliderBitmap 宽度

        set(value) {

            field= value

            srcRect.right= field

        }

    private var sliderHeight= 0 // 滑块高度,即 sliderBitmap 高度

        set(value) {

            field= value

            srcRect.bottom= field

        }

    private var sliderBitmap:Bitmap

        set(value) {

            field= value

            sliderWidth= field.width

            sliderHeight= field.height

        }

    private val paint by lazy { Paint() }

    private fun getSliderPaint():Paint {

        paint.reset()

        paint.isAntiAlias = true

        return paint

    }

    private fun getLinePaint(colorRes:Int):Paint {

        paint.reset()

        paint.style = Paint.Style.FILL

        paint.color = ContextCompat.getColor(context, colorRes)

        paint.isAntiAlias = true

        return paint

    }

    private var srcRect= Rect(0, 0, 0, 0)

    private var leftDst= RectF(0f, 0f, 0f, 0f)

    private var rightDst= RectF(0f, 0f, 0f, 0f)

    /** * 左滑块位置矩形 */

    private fun getLeftDst():RectF {

        leftDst.set(

            currentLeft* lineWidth, (height - srcRect.height()) / 2f,

            currentLeft* lineWidth+ srcRect.width(), (height + srcRect.height()) / 2f

        )

        return leftDst

    }

    /** * 右滑块位置矩形 */

    private fun getRightDst():RectF {

        rightDst.set(

            currentRight* lineWidth, (height - srcRect.height()) / 2f,

            currentRight* lineWidth+ srcRect.width(), (height + srcRect.height()) / 2f

        )

        return rightDst

    }

    private fun getLineLeft():Float {

        return currentLeft* lineWidth+ sliderWidth

    }

    private fun getLineRight():Float {

        return currentRight* lineWidth

    }

    override fun onSizeChanged(w:Int, h:Int, oldw:Int, oldh:Int) {

        super.onSizeChanged(w, h, oldw, oldh)

        lineWidth= width - sliderBitmap.width

    }

    override fun onDraw(canvas:Canvas?) {

        // 绘制底线

        canvas?.drawRect(

            (width - lineWidth) / 2f, (height - lineHeight) / 2f,

            (width + lineWidth) / 2f, (height + lineHeight) / 2f,

            getLinePaint(R.color.whiteblue_theme_half_dark)

        )

        // 绘制进度线

        canvas?.drawRect(

            getLineLeft(), (height - lineHeight) / 2f,

            getLineRight(), (height + lineHeight) / 2f,

            getLinePaint(R.color.colorAccent)

        )

        // 绘制左滑块

        canvas?.drawBitmap(sliderBitmap, srcRect, getLeftDst(), getSliderPaint())

        // 绘制右滑块

        canvas?.drawBitmap(sliderBitmap, srcRect, getRightDst(), getSliderPaint())

    }

    override fun onTouchEvent(event:MotionEvent?):Boolean {

        when (event?.action) {

            MotionEvent.ACTION_DOWN -> {

                return checkClick(event.x, event.y)

            }

            MotionEvent.ACTION_MOVE -> {

                val currentProgress= checkedCurrentProgress(event.x)

                when (slidingDir) {

                    LEFT-> {

                        if (currentProgress>= currentRight) {

                            currentLeft= currentRight

                            invalidate()

                        } else {

                            currentLeft= currentProgress

                            invalidate()

                        }

                    }

                    RIGHT-> {

                        if (currentProgress<= currentLeft) {

                            currentRight= currentLeft

                            invalidate()

                        } else {

                            currentRight= currentProgress

                            invalidate()

                        }

                    }

                }

                onSliderListener(slidingDir, currentLeft, currentRight)

                return true

            }

        }

        return false

    }

    /**

      * 经校验的当前点击的范围

      * @param eventX 点击的 X 坐标

      * @return 0 - 1f

      */

    private fun checkedCurrentProgress(eventX:Float):Float {

        val progress= (eventX - sliderBitmap.width / 2) / lineWidth

        return when {

            progress< 0 -> 0f

            progress> 1 -> 1f

            else -> progress

        }

    }

    /**

      * 检查点击是否有效,有效范围为左右滑块位置,并判断滑动方向,优先向左 (要更改

        优先级把 when 里面顺序调换或另写一个函数去判断)

      * @param eventX 点击的 X 坐标

      * @param eventY 点击的 Y 坐标

      * @return true 点击有效 else 点击无效

      */

    private fun checkClick(eventX:Float, eventY:Float):Boolean {

        val leftXMin= currentLeft* lineWidth

        val leftXMax= currentLeft* lineWidth+ srcRect.width()

        val rightXMin= currentRight* lineWidth

        val rightXMax= currentRight* lineWidth+ srcRect.width()

        val yMin= (height - srcRect.height()) / 2f

        val yMax= (height + srcRect.height()) / 2f

        return if (eventY in yMin..yMax) {

            when (eventX) {

                in leftXMin..leftXMax-> {

                    slidingDir= if (currentLeft== 0f && currentRight== 0f) RIGHTelse LEFT

                    true

                }

                in rightXMin..rightXMax-> {

                    slidingDir= if (currentRight== 1f && currentLeft== 1f) LEFTelse RIGHT

                    true

                }

                else -> false

            }

        } else false

    }

    /** * 最后别忘了清除 bitmap 数据引用 */

    fun recycle() {

        sliderBitmap.recycle()

    }

}

滑动时回调: onSliderListener:(slidingDir:Int, currentLeft:Float, currentRight:Float) -> Unit

slidingDir: 滑动方向,DoubleSeekBarView.LEFT | DoubleSeekBarView.RIGHT

currentLeft: 当前左滑块位置,0-1f

currentRight: 同理右滑块

上一篇下一篇

猜你喜欢

热点阅读