android: 一次自繪控制元件的體驗

南郭竽發表於2018-06-30

一個盆友在 qq 上面給我一個截圖,問我有沒有見過這種效果。我一看,貌似不太難,雖然我並不熟悉自定義控制元件,但是網上的教程很多,於是決定實現一下。

原效果圖

這個就是給我的截圖。不是很清晰,也不完整。但是重點突出出來了。

於是,我看了看 HenCoder 的教程1,決定實現一下。(當然,實現期間,也翻閱了一下其他人的部落格)

我實現的效果如下:

這裡寫圖片描述

大體實現貼一下:

@Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        this.mWidth = w;
        this.mHeight = h;

        min = Math.min(mWidth, mHeight);
        tableList = DynamicData.getViewSource();
        paint = new Paint();
        LogUtils.w(tableList);

        oval = new RectF(0, 0, min, min);
        icon = new RectF();

        diffLeft = min / 10;
        diffRight = diffLeft;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawLeft(canvas);

        iconLength = 1f * min / 17;
        int height = (int) (iconLength / 2);
        for (Table t : tableList) {
            float left = min + diffLeft;
            float right = left + iconLength;
            float top = 0;
            float bottom = top + iconLength;
            paint.setColor(t.color);
            icon.set(left, height + top, right, height + bottom);
            canvas.drawRect(icon, paint);

            paint.setTextSize(iconLength);
            paint.setColor(Color.BLACK);
            canvas.drawText(t.name + "\t" + (int) t.percent + "%",
                    right + diffRight, height + bottom, paint);
            height += iconLength * 3;
        }
    }

    private void drawLeft(Canvas canvas) {
        paint.setColor(Color.parseColor("#dbdbdb"));
        canvas.drawRect(oval, paint);
        int start = 180;
        for (Table t : tableList) {
            float angle = (float) t.percent / 100 * 360;
            paint.setColor(t.color);
            paint.setStrokeCap(Paint.Cap.ROUND);

            scaleOval(t.scale);
            canvas.drawArc(oval, start, angle,
                    true, paint);
            start += angle;
        }
        paint.setColor(Color.WHITE);
        canvas.drawCircle(1f * min / 2, 1f * min / 2, min * 1f / 12, paint);
        scaleOval(1); // 要加這一句,不然 onStop 後再進來就變小了
    }

    private void scaleOval(float scale) {
        oval.set(0, 0, min, min);
        float left = 1f * min * (1 - scale) / 2;
        float top = 1f * min * (1 - scale) / 2;
        float right = 1f * min * scale + left;
        float bottom = 1f * min * scale + top;
        oval.set(left, top, right, bottom);
    }

嗯,裡面的邏輯不復雜,主要是對系統 api 的呼叫,然後計算一下,每次畫的起始位置。

其實,這種東西,我之前看到也會害怕,但是,真的很一般,不難的。


不過,這裡還是有需要優化的地方,比如最小尺寸,如果設定成 wrap_conent 怎麼處理,等等。我這裡的邏輯全部是 onDraw()需要的,其他的,我沒有去實現。

如果想看完整原始碼:獻醜了,戳我。。。

相關文章