双向水平滑动条
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: 同理右滑块