Android绘制圆环解锁

120 阅读1分钟

2022-10-26-13-53-27-image.png

核心功能点如下:

计算角度

使用三角函数可以计算出触摸坐标点所对应的角度

/**
 * 计算当前的角度
 * @param x 触摸坐标点
 * @param y 触摸坐标点
 */
private fun updateCurrentAngle(x: Float, y: Float): Double {
    val pointX = x - mCenterX
    val pointY = y - mCenterY
    return if (pointX >= 0 && pointY <= 0) {
        //第一象限
        "第一象限".print()
        atan((pointX / -pointY)).run {
            Math.toDegrees(this.toDouble()) + 90f + mOffsetAngle
        }.apply {
            "角度:$this".print()
        }
    } else if (pointX <= 0 && pointY >= 0) {
        //第三象限
        "第三象限".print()
        atan(-pointX / pointY).run {
            Math.toDegrees(this.toDouble())
        }.run {
            if (this >= 90f) {
                this - 90f
            } else {
                this + 270
            } + mOffsetAngle
        }.apply {
            "角度:$this".print()
        }
    } else if (pointX <= 0 && pointY <= 0) {
        //第四象限
        "第四象限".print()
        atan(-pointY / -pointX).run {
            Math.toDegrees(this.toDouble()) + mOffsetAngle + 360f
        }.apply {
            "角度:$this".print()
        }
    } else {
        //第二象限
        "第二象限".print()
        atan(pointY / pointX).run {
            Math.toDegrees(this.toDouble()) + 180 + mOffsetAngle
        }.apply {
            "角度:$this".print()
        }
    }
}

计算触摸点射线到圆弧的交叉点

/**
 * 依圆心坐标,半径,扇形角度,计算出扇形终射线与圆弧交叉点的xy坐标
 *
 * @param cirX     圆centerX
 * @param cirY     圆centerY
 * @param radius   圆半径
 * @param cirAngle 当前弧角度
 * @return 扇形终射线与圆弧交叉点的xy坐标
 */
private fun calcArcEndPointXY(
    cirX: Float, cirY: Float, radius: Float, cirAngle: Float
): PointF {
    val posX: Float
    val posY: Float
    //将角度转换为弧度
    var arcAngle = (Math.PI * cirAngle / 180.0).toFloat()
    if (cirAngle < 90) {
        posX = cirX + cos(arcAngle.toDouble()).toFloat() * radius
        posY = cirY + sin(arcAngle.toDouble()).toFloat() * radius
    } else if (cirAngle == 90f) {
        posX = cirX
        posY = cirY + radius
    } else if (cirAngle > 90 && cirAngle < 180) {
        arcAngle = (Math.PI * (180 - cirAngle) / 180.0).toFloat()
        posX = cirX - cos(arcAngle.toDouble()).toFloat() * radius
        posY = cirY + sin(arcAngle.toDouble()).toFloat() * radius
    } else if (cirAngle == 180f) {
        posX = cirX - radius
        posY = cirY
    } else if (cirAngle > 180 && cirAngle < 270) {
        arcAngle = (Math.PI * (cirAngle - 180) / 180.0).toFloat()
        posX = cirX - cos(arcAngle.toDouble()).toFloat() * radius
        posY = cirY - sin(arcAngle.toDouble()).toFloat() * radius
    } else if (cirAngle == 270f) {
        posX = cirX
        posY = cirY - radius
    } else {
        arcAngle = (Math.PI * (360 - cirAngle) / 180.0).toFloat()
        posX = cirX + cos(arcAngle.toDouble()).toFloat() * radius
        posY = cirY - sin(arcAngle.toDouble()).toFloat() * radius
    }
    return PointF(posX, posY)
}

绘制贴图

mArcPath.apply {
    val r = max(measuredWidth, measuredHeight) / 2f
    canvas.save()
    reset()
    moveTo(mCenterX, mCenterY)
    val startPoint = calcArcEndPointXY(mCenterX, mCenterY, r, mOffsetAngle)
    lineTo(startPoint.x, startPoint.y)
    addArc(mRingDrawableRectF, mOffsetAngle - 12f, mSweptAngle.toFloat() + 12f)
    lineTo(mCenterX, mCenterY)
    close()
}

if (mSweptAngle > 1f) {
    canvas.clipPath(mArcPath)
    mRingDrawable.draw(canvas)
    canvas.restore()
}