美文网首页
双向水平滑动条

双向水平滑动条

作者: 案玉璃青 | 来源:发表于2019-12-16 09:17 被阅读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: 同理右滑块

    相关文章

      网友评论

          本文标题:双向水平滑动条

          本文链接:https://www.haomeiwen.com/subject/xejrnctx.html