分析
结构拆分
- 灰色背景:圆角矩形
- 白色进度条:圆角矩形
- 进度条按钮:图片
- 提示文字
注意
@SuppressLint("ResourceAsColor")
class CustomSlideButton @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
/**
* 自定义属性相关
*/
private var thumbImg by Delegates.notNull<Int>()
private var bgColor by Delegates.notNull<Int>()
private var pgColor by Delegates.notNull<Int>()
private var thumbColor by Delegates.notNull<Int>()
private var centerTextColor by Delegates.notNull<Int>()
private var centerTextSize by Delegates.notNull<Int>()
private var centerText:String
private var thumbRadius by Delegates.notNull<Int>()
/**
* 画笔
*/
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
<declare-styleable name="CustomSlideBarStyle">
<attr name="thumbImg" format="reference"/>
<attr name="thumbColor" format="color"/>
<attr name="backgroundFull" format="color"/>
<attr name="progressColor" format="color"/>
<attr name="centerText" format="string"/>
<attr name="centerTextColor" format="color"/>
<attr name="centerTextSize" format="dimension"/>
<attr name="leftFollowRight" format="boolean"/>
</declare-styleable>