Android动态View(仿万能钥匙信号检测View)的实现

    实现类似万能钥匙信号检测View,主要在于图片旋转的控制,而图片的旋转等操作则离不开Matrix。
本篇充分利用的Matrx的各种变换,以实现动态旋转的view。

先上效果图:
Android动态View(仿万能钥匙信号检测View)的实现_第1张图片

一、 实现动态旋转指示的主要过程如下:

(1)计算缩放倍数、旋转角度,使相关图片调整到设置大小

注意:指针、背景、指示表的中心

其中主要涉及到的Matrix的使用:

        //缩放倍数
        val scaleX = mViewWidth / mSpeedPointerBitmapSource!!.width.toFloat()
        val scaleY = mViewHeight / mSpeedPointerBitmapSource!!.height.toFloat()
        //缩放
        mSpeedBitmapMatrix!!.postScale(scaleX, scaleY)
        //平移
        mSpeedBitmapMatrix!!.postTranslate(paddingLeft.toFloat(), paddingTop.toFloat())
        //绕中心旋转
        mSpeedBitmapMatrix!!.postRotate(mCurrentAngle.toFloat(), mViewCenterX, mViewCenterY)

        //外层
        val matrix = Matrix()
        matrix.postScale(mViewWidth.toFloat() / mSpeedProcessBitmapSource!!.width, mViewHeight.toFloat() / mSpeedProcessBitmapSource!!.height.toFloat())
        mSpeedProcessBitmap = Bitmap.createBitmap(mSpeedProcessBitmapSource!!, 0, 0, mSpeedProcessBitmapSource!!.width, mSpeedProcessBitmapSource!!.height, matrix, true)
        //刻度盘
        mSpeedDialBitmap = Bitmap.createScaledBitmap(mSpeedDialBitmap, mViewWidth, mViewHeight, false)

(2)绘制指针、进度、刻度盘等图片及文字信息

       mPaint.isAntiAlias = true
        mPaint.color = ContextCompat.getColor(context, android.R.color.white)
        //绘制刻度盘
        canvas?.drawBitmap(mSpeedDialBitmap, paddingLeft.toFloat(), paddingTop.toFloat(), null)

        //绘制圆环
        canvas?.drawBitmap(mSpeedProcessBitmap, paddingLeft.toFloat(), paddingTop.toFloat(), null)
        //绘制指针
        canvas?.drawBitmap(mSpeedPointerBitmapSource, mSpeedBitmapMatrix, null)
        //绘制中间指示数值
        mPaint.textSize = DensityUtils.sp2px(context!!, 20f).toFloat()
        mPaint.textAlign = Paint.Align.CENTER
        canvas?.drawText("${(mCurrentAngle / MAX_ANGLE_DEFAULT.toFloat() * 100).toInt()}%", mViewCenterX, mViewCenterY + DensityUtils.sp2px(context!!, 6f).toFloat(), mPaint)
        mPaint.color = ContextCompat.getColor(context, android.R.color.darker_gray)
        //绘制底部名称
        canvas?.drawText(mTargetName, mViewCenterX, height - paddingBottom - DensityUtils.sp2px(context!!, 20f).toFloat(), mPaint)

(3)绘制遮挡层

        //设置遮挡圆环的参数
        mPaint.style = Paint.Style.STROKE
        mPaint.alpha = 240
        //环形的宽度
        mPaint.strokeWidth = DensityUtils.dp2px(context, 15f).toFloat()
        val x = mViewWidth / 2f - DensityUtils.dp2px(context, 41.5f)//41.5dp是图片边缘距离圆环处的距离
        val y = mViewHeight / 2f - DensityUtils.dp2px(context, 41.5f)
        //绘制圆环遮挡层
        val rectF = RectF(mViewCenterX - x, mViewCenterY - y, mViewCenterY + x, mViewCenterY + y)
        canvas?.drawArc(rectF, 135f + mCurrentAngle, MAX_ANGLE_DEFAULT - mCurrentAngle.toFloat(), false, mPaint)

(4)计算遮挡层的旋转角度

 //初次会转动到最大角度后,返回到指定角度
 if (!firstCheck) {
            mCurrentAngle += mAngleSpeed
            if (mCurrentAngle >= MAX_ANGLE_DEFAULT) {
                firstCheck = true
            }
        } else {
            if (mCurrentAngle >= mMaxAngle) {
                mCurrentAngle -= mAngleSpeed
            }
        }

二、 WRAP_CONTENT及PADDING的适配

(1)WRAP_CONTENT适配

当设置WRAP_CONTENT时,则使用默认的大小。

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        val widthMeasureSpecMode = MeasureSpec.getMode(widthMeasureSpec)
        val widthMeasureSpecSize = MeasureSpec.getSize(widthMeasureSpec)
        val heightMeasureSpecMode = MeasureSpec.getMode(heightMeasureSpec)
        val heightMeasureSpecSize = MeasureSpec.getSize(heightMeasureSpec)
        if (widthMeasureSpecMode == MeasureSpec.AT_MOST && heightMeasureSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(mMaxWidth, mMaxHeight)
        } else if (widthMeasureSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(mMaxWidth, heightMeasureSpecSize)
        } else if (heightMeasureSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(widthMeasureSpecSize, mMaxHeight)
        }
    }

(2)PADDING的适配

计算图片的大小时计入padding

        mViewWidth = width - paddingLeft - paddingRight
        mViewHeight = height - paddingTop - paddingBottom

三、说明

(1)关于遮挡层

由于遮挡层的宽度无法算精确,可能在部分手机上无法完全遮挡住指示,可以根据具体情况调整遮挡层的宽度

(2)代码

代码已上传github,如需源码,可访问Github SignalView

你可能感兴趣的:(Android)