效果图
分析
结构拆分
- 灰色背景:圆角矩形
- 白色进度条:圆角矩形
- 进度条按钮:图片
- 提示文字
注意
- 快速滑动时X轴返回值超过最大可滑动距离要做处理
@SuppressLint("ResourceAsColor")
class CustomSlideButton @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
/**
* 自定义属性相关
*/
private var thumbImg by Delegates.notNull()
private var bgColor by Delegates.notNull()
private var pgColor by Delegates.notNull()
private var thumbColor by Delegates.notNull()
private var centerTextColor by Delegates.notNull()
private var centerTextSize by Delegates.notNull()
private var centerText:String
private var thumbRadius by Delegates.notNull()
/**
* 画笔
*/
private val paintBg:Paint by lazy { Paint() }
private val paintPg:Paint by lazy { Paint() }
private val paintCenterText:Paint by lazy { Paint() }
private lateinit var ovalFull:RectF
private lateinit var ovalProgress:RectF
private var downX:Float=0f //按下位置x
private var downY:Float=0f //按下位置y
private var progressRight=0 //内圈右边距离
private var progressBarPadding=0 //内圈padding
private var thumbBitmap: Bitmap //thump 图片
private var finishSlide=false //完成滑动
private var followMode=false //左边跟随滑动
private var moveX=0f //当前移动距离
private var maxMoveX=0 //最大移动距离
private var viewWidth:Int=0 //width
private var viewHeight:Int=0 //height
private var anmitonReset:ValueAnimator?=null //重置view动画
interface SlideFinishListener {
fun finishSlide()
}
private var slideFinishListener: SlideFinishListener? = null
fun setSlideFinishListener(slideFinishListener: SlideFinishListener?) {
this.slideFinishListener = slideFinishListener
}
init {
val typeArray =
context.theme.obtainStyledAttributes(attrs, R.styleable.CustomSlideBarStyle, defStyleAttr, 0)
thumbImg=
typeArray.getResourceId(R.styleable.CustomSlideBarStyle_thumbImg,R.mipmap.ic_open_net)
bgColor = typeArray.getColor(R.styleable.CustomSlideBarStyle_backgroundFull, R.color.white_70)
pgColor =
typeArray.getColor(R.styleable.CustomSlideBarStyle_progressColor, R.color.white)
thumbColor =
typeArray.getColor(R.styleable.CustomSlideBarStyle_thumbColor, R.color.white)
centerTextColor=
typeArray.getColor(R.styleable.CustomSlideBarStyle_centerTextColor,R.color.black)
centerTextSize=
typeArray.getDimensionPixelSize(R.styleable.CustomSlideBarStyle_centerTextSize,27)
centerText=
typeArray.getString(R.styleable.CustomSlideBarStyle_centerText).toString()
followMode=
typeArray.getBoolean(R.styleable.CustomSlideBarStyle_leftFollowRight,true)
typeArray.recycle()
progressBarPadding=context.resources.getDimensionPixelSize(R.dimen.silide_bar_padding)
thumbBitmap=BitmapFactory.decodeResource(context.resources,thumbImg)
paintBg.color = bgColor
paintBg.alpha= (255*0.7).toInt()
paintCenterText.style=Paint.Style.FILL
paintBg.isAntiAlias=true
paintPg.color=pgColor
paintPg.style=Paint.Style.FILL
paintPg.isAntiAlias=true
paintCenterText.color=centerTextColor
paintCenterText.style=Paint.Style.FILL
paintCenterText.textSize= centerTextSize.toFloat()
paintCenterText.textAlign = Paint.Align.CENTER
paintCenterText.isFakeBoldText=true //粗体
paintCenterText.isAntiAlias=true
}
@SuppressLint("DrawAllocation")
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
//画背景
ovalFull= RectF(if(followMode)moveX else 0f,0f, viewWidth.toFloat(), viewHeight.toFloat())
canvas.drawRoundRect(ovalFull,thumbRadius.toFloat(),thumbRadius.toFloat(),paintBg)
//画文字
drawCenterText(canvas)
//画进度条
ovalProgress= RectF(if(followMode) progressBarPadding.toFloat()+ moveX else progressBarPadding.toFloat(),progressBarPadding.toFloat(), progressRight.toFloat()+moveX, viewHeight.toFloat()-progressBarPadding.toFloat())
canvas.drawRoundRect(ovalProgress,thumbRadius.toFloat(),thumbRadius.toFloat(),paintPg)
//画进度按钮
canvas.drawBitmap(thumbBitmap,progressBarPadding.toFloat()+moveX,progressBarPadding.toFloat(),null)
}
/**
* 画中间文字
*/
private fun drawCenterText(canvas: Canvas) {
if (followMode && moveX!=0f){//跟随滑动模式下,滑动到最左边才显示中间文字
return
}
//画文字
//计算baseline
val fontMetrics: FontMetrics = paintCenterText.fontMetrics
val distance = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom
val baseline: Float = ovalFull.centerY() + distance
// canvas.drawText(centerText, ovalFull.centerX(), baseline, paintCenterText)
canvas.drawText(
centerText,
viewHeight + (viewWidth - viewHeight) / 2f,
baseline,
paintCenterText
)
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
thumbRadius=h/2
viewWidth=w
viewHeight=h
progressRight=h-progressBarPadding
maxMoveX=w-h
//压缩图片到指定大小
thumbBitmap= BitmapUtil.zoomImg2(thumbBitmap,h-progressBarPadding*2,h-progressBarPadding*2)
}
override fun onTouchEvent(event: MotionEvent): Boolean {
when(event.action){
MotionEvent.ACTION_DOWN->{
//按下位置在按钮范围内才接收
if (event.x in 0f..viewHeight.toFloat() && event.y in 0f..viewHeight.toFloat()){
downX= event.x
downY= event.y
}
}
MotionEvent.ACTION_MOVE->{
if (downX!=0f && downY!=0f){
var currMovex=event.x-downX
if (currMovex<0){currMovex=0f}
if (currMovex.toInt() in 0..maxMoveX){
moveX=currMovex
invalidate()
}else if(currMovex.toInt()>=maxMoveX){
if (!finishSlide){
moveX= maxMoveX.toFloat() //快速滑动超出最大可移动范围时,直接绘制到最右端
invalidate()
finishSlide=true
slideFinishListener?.finishSlide()
}
}
}
}
MotionEvent.ACTION_UP->{
if (downX!=0f && downY!=0f && !finishSlide){
downX=0f
downY=0f
if (moveX!=0f){
resetSlideButton()//未完成滑动,重置view
}
}
}
}
return true
}
private fun resetSlideButton(){
if (anmitonReset?.isRunning == true){
return
}
anmitonReset = ValueAnimator.ofFloat(moveX,0f)
anmitonReset!!.duration = 200
anmitonReset!!.interpolator= AccelerateInterpolator()
anmitonReset!!.addUpdateListener { animation ->
moveX = animation.animatedValue as Float
invalidate()
}
anmitonReset!!.start()
}
public fun resetState(){
if (anmitonReset?.isRunning == true){
anmitonReset?.cancel()
}
finishSlide=false
downY=0f
downX=0f
resetSlideButton()
}
}
attrs.xml