美文网首页
仿IOS关机滑动View

仿IOS关机滑动View

作者: 淡意的温柔丶 | 来源:发表于2021-09-29 11:38 被阅读0次
    效果图

    分析

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

    相关文章

      网友评论

          本文标题:仿IOS关机滑动View

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