仿IOS关机滑动View

效果图

分析

结构拆分
  • 灰色背景:圆角矩形
  • 白色进度条:圆角矩形
  • 进度条按钮:图片
  • 提示文字
注意
  • 快速滑动时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


        
        
        
        
        
        
        
        

你可能感兴趣的:(仿IOS关机滑动View)