Android輪盤控件-自定義

圓形輪盤選擇器(CircleWheelView)

背景:產(chǎn)品需要對游戲的按鍵做成圓形,且可以下發(fā),點擊效果相當于操作按鍵
初期參照過市面上的開源,沒有完全匹配要求的,最終還是自己動手做了一個,整理下了總體實現(xiàn)的思路和關鍵點

先上視頻

輪盤視頻.gif

整體思路

1.繪制扇形區(qū)域和中心圓形區(qū)域

2.手指觸摸位置判斷(中心,扇形區(qū)域),選中區(qū)域重新繪制背景色

3.繪制中心圓弧和扇形之間白色線條

4.扇形區(qū)域文字繪制

5.為了特效,設計給的一些背景圖的繪制

特別注意點

1.Android中扇形繪制起始點默認是水平方向順時針方向,開始繪制

2.為了方便計算,canvas最好先移動中心位置( canvas.translate(mWRadius, mWRadius)),原點坐標才會為(0,0)

核心代碼解析

1.扇形繪制(無中心部分): 1- 扇形 2-中心圓形 使用 Path.Op.DIFFERENCE 屬性就是代表

繪制圖 = 圖1--圖1和圖2的交集

* 獲取繪制弧度所需要的path

     * @param in
     * @param out
     * @param startAngle
     * @param angle
     * @return
     */
    private fun getArcPath(
        inSide: RectF,
        out: RectF,
        startAngle: Float,
        angle: Float
    ): Path {
        val path1 = Path()
        path1.moveTo(inSide.centerX(), inSide.centerY())
        path1.addCircle(inSide.centerX(), inSide.centerY(), 2 * mCenterRadiu / 3, Path.Direction.CW)
        val path2 = Path()
        path2.moveTo(out.centerX(), out.centerY())
        path2.arcTo(out, startAngle, angle)
        val path = Path()
        path.op(path2, path1, Path.Op.DIFFERENCE)
        return path
    }

2.扇形區(qū)域的保存,由于扇形的path已經(jīng)保存在 mRegionList,后面直接根據(jù)手指的(x,y)判斷所在扇形區(qū)域根據(jù)扇形的path設置

/**
     * 判斷扇形區(qū)域
     */
    private fun inAreaPos(event: MotionEvent, regions: MutableList<Region>): Int {
        val x: Float = event.x - mWRadius
        val y: Float = event.y - mWRadius
        for (i in mRegionList.indices) {
            if (regions[i].contains(x.toInt(), y.toInt())) {
                return i
            }
        }
        return -1
    }

3.扇形中的文字繪制 (為了文字居中,首先獲取角度的一半,獲取中心圓形到圓弧2點的中間坐標,然后在中間坐標繪制文字)

 /**
     * 扇形畫文字
     */
    private fun drawText(
        mCanvas: Canvas,
        textAngle: Float,
        kinds: String,
        mPaint: Paint,
        mRadius: Float,
        mCenTer: Float
    ) {
        val rect = Rect()
        mPaint.textSize = 38f
        var fontMetrics = mPaint.fontMetrics
        val dy = (fontMetrics.bottom - fontMetrics.top) / 2f - fontMetrics.bottom
        mPaint.getTextBounds(kinds, 0, kinds.length, rect)
        val pointEnd = PointF()
        val pointStart = PointF()
        val pointCengter = PointF()
        var x = 0.0
        var y = 0.0
        if (textAngle in 0.0..90.0) { //畫布坐標系第一象限(數(shù)學坐標系第四象限)
            x = cos(Math.toRadians(textAngle.toDouble()))
            y = sin(Math.toRadians(textAngle.toDouble()))
        } else if (textAngle > 90 && textAngle <= 180) { //畫布坐標系第二象限(數(shù)學坐標系第三象限)
            x = (-cos(Math.toRadians(180 - textAngle.toDouble())))
            y = (sin(Math.toRadians(180 - textAngle.toDouble())))
        } else if (textAngle > 180 && textAngle <= 270) { //畫布坐標系第三象限(數(shù)學坐標系第二象限)
            x = (-cos(Math.toRadians(textAngle - 180.toDouble())))
            y = (-sin(Math.toRadians(textAngle - 180.toDouble())))
        } else { //畫布坐標系第四象限(數(shù)學坐標系第一象限)
            x = (cos(Math.toRadians(360 - textAngle.toDouble())))
            y = (-sin(Math.toRadians(360 - textAngle.toDouble())))
        }
        pointStart.set((x * mCenTer).toFloat(), (y * mCenTer).toFloat())
        pointEnd.set((x * mRadius).toFloat(), (y * mRadius).toFloat())
        pointCengter.set((pointEnd.x + pointStart.x) / 2, (pointEnd.y + pointStart.y) / 2)
        Log.i(TAG, "X= ${(pointEnd.x + pointStart.x) / 2}  y=${(pointEnd.y + pointStart.y) / 2}")

        mCanvas.drawText(
            kinds,
            pointCengter.x,
            pointCengter.y + dy,
            mPaint
        )
    }

4.圓形中心和弧形間線條的繪制(思路:根據(jù)角度找到內(nèi)部圓形的坐標(x1,y2),在找到圓弧上的點(x2,y2),path連起來,然后繪制線條)

    /**
     * 圓形中心和弧形間線條的繪制
     */
    private fun drawLinePath(
        canvas: Canvas,
        radius: Float,
        radiusN: Float,
        angele: Double
    ) {
        var linePath = Path()
        val paint = Paint()
        paint.color = Color.parseColor("#4dd8d8d8")
        paint.style = Paint.Style.STROKE
        paint.isAntiAlias = true
        paint.strokeWidth = 3f
        when (angele) {
            in 0.0f..90.0f -> {
                linePath.moveTo(
                    radiusN * cos(Math.toRadians(angele).toFloat()),
                    -radiusN * sin(Math.toRadians(angele).toFloat())
                )
                linePath.lineTo(
                    radius * cos(Math.toRadians(angele).toFloat()),
                    -radius * sin(Math.toRadians(angele).toFloat())
                )
            }
            in 90.0f..180f -> {
                linePath.moveTo(
                    -radiusN * sin(Math.toRadians(angele - 90f).toFloat()),
                    -radiusN * cos(Math.toRadians(angele - 90).toFloat())
                )
                linePath.lineTo(
                    -radius * sin(Math.toRadians(angele - 90).toFloat()),
                    -radius * cos(Math.toRadians(angele - 90).toFloat())
                )
            }
            in 180.0f..270f -> {
                linePath.moveTo(
                    -radiusN * cos(Math.toRadians(angele - 180).toFloat()),
                    radiusN * sin(Math.toRadians(angele - 180).toFloat())
                )
                linePath.lineTo(
                    -radius * cos(Math.toRadians(angele - 180).toFloat()),
                    radius * sin(Math.toRadians(angele - 180).toFloat())
                )
            }
            in 270.0f..360f -> {
                linePath.moveTo(
                    radiusN * sin(Math.toRadians(angele - 270).toFloat()),
                    radiusN * cos(Math.toRadians(angele - 270).toFloat())
                )
                linePath.lineTo(
                    radius * sin(Math.toRadians(angele - 270).toFloat()),
                    radius * cos(Math.toRadians(angele - 270).toFloat())
                )
            }
        }
        canvas.drawPath(linePath, paint)
    }

5.中間文字的繪制和中心圓形位置選中和未選中用的是圖片繪制,這個就沒啥可說的了

6.其實該控件還支持合并,拆解,縮放,拖拽 ,但是為了簡潔點,都已經(jīng)被我干掉了

最后還要感謝下,控件制作過程中,給予我思路靈感的同事 阿勇

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容