Android自定義半三環View的實現

本文實現自定義半三環View效果。主要目的是總結實現過程當中的思路以及一些須要注意的地方。 首先,效果圖:java

實現邏輯

  • 從新指定View寬高
  • 繪製外圓圓弧背景及進度
  • 繪製中圓圓弧背景及進度
  • 繪製內圓圓弧背景及進度

知識點

onMeasurecanvas

  • 用於測量View的大小。建立時View無需測量,當將這個View放入一個容器(父控件)時候才須要測量,而測量方法由父控件調用。當控件的父控件要放置該控件的時候,父控件會調用子控件的onMeasure方法肯定子控件須要的空間大小,而後傳入widthMeasureSpec和heightMeasureSpec來告訴子控件可得到的空間大小,子控件經過這兩個參數就能夠測量自身的寬高了。

setMeasuredDimensionide

  • 用於從新設置View寬高

Canvas#drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)post

  • 繪製以oval爲邊界的圓弧

onDrawthis

  • 用來肯定View長什麼樣。onDraw繪製過程以下:
    1. Draw the background(繪製背景)
    2. If necessary, save the canvas’ layers to prepare for fading(若是須要,爲保存這層爲邊緣的滑動效果做準備)
    3. Draw view’s content(繪製內容)
    4. Draw children(繪製子View)
    5. If necessary, draw the fading edges and restore layers(若是須要,繪製邊緣效果而且保存圖層)
    6. Draw decorations (scrollbars for instance)(繪製邊框,好比scrollbars,TextView)

主要代碼

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    // 根據父控件傳遞的widthMeasureSpec和heightMeasureSpec調用MeasureSpec.getSize測量自身寬高
    int measureWidth = MeasureSpec.getSize(widthMeasureSpec);
    int measureHeight = MeasureSpec.getSize(heightMeasureSpec);
    int finalWidth = measureWidth;
    int finalHeight = measureHeight;
    // 根據自身寬高從新計算新的寬高,使新的寬高比爲2:1
    if (measureWidth >= measureHeight * 2) {
        finalWidth = measureHeight * 2;
    } else {
        finalHeight = measureWidth / 2;
    }
    // 設置View新的寬高
    setMeasuredDimension(finalWidth, finalHeight);
}
複製代碼
/** * 繪製圓弧 * @param canvas * @param progress 進度 * @param color 進度顏色 * @param radius 圓弧半徑 */
private void drawArc(Canvas canvas, float progress, int color, float radius){
    // 圓心
    mXCenter = getWidth() / 2;
    mYCenter = getHeight() ;

    mPaint.setColor(mBackgroundArcColor);
    // 構造邊界矩形
    RectF oval = new RectF();
    oval.left = (mXCenter - radius);
    oval.top = (mYCenter - radius);
    oval.right = mXCenter + radius;
    oval.bottom = radius * 2 + (mYCenter - radius);
    //繪製圓弧背景
    canvas.drawArc(oval, -180, 180, false, mPaint);

    //繪製圓弧進度
    float showDegree = progress / 100 * 180;
    mPaint.setColor(color);
    canvas.drawArc(oval, -180, showDegree, false, mPaint);
}
複製代碼
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    // 初始半徑
    float originalRadius = getWidth() * .5f;
    // 畫筆半寬
    float halfArcStokeWidth = mArcStrokeWidth * .5f;

    // 外圓環半徑=初始半徑-畫筆半寬
    float outSideArcRadius = originalRadius - halfArcStokeWidth;
    drawArc(canvas, mOutsideProgress, mOutsideArcColor, outSideArcRadius);

    // 中圓環半徑=外圓的半徑-圓環偏移值-畫筆半寬
    float middleArcRadius = outSideArcRadius - mArcOffset - halfArcStokeWidth;
    drawArc(canvas, mMiddleProgress, mMiddleArcColor, middleArcRadius);

    // 內圓環半徑=中圓的半徑-圓環偏移值-畫筆半寬
    float insideArcRadius = middleArcRadius - mArcOffset - halfArcStokeWidth;
    drawArc(canvas, mInsideProgress, mInsideArcColor, insideArcRadius);
}
複製代碼

所有代碼

ThreeArcView.javaspa

public class ThreeArcView extends View {

    //圓弧畫筆
    private Paint mPaint;
    //背景圓環顏色
    private int mBackgroundArcColor;
    //外圓環顏色
    private int mOutsideArcColor;
    //中圓環顏色
    private int mMiddleArcColor;
    //內圓環顏色
    private int mInsideArcColor;
    //外圓展現弧度
    private float mOutsideProgress;
    //中圓展現弧度
    private float mMiddleProgress;
    //內圓展現弧度
    private float mInsideProgress;
    //圓弧寬度
    private float mArcStrokeWidth;
    //圓偏移值
    private float mArcOffset;

    // 圓心x座標
    private int mXCenter;
    // 圓心y座標
    private int mYCenter;

    public ThreeArcView(Context context) {
        this(context, null);
    }

    public ThreeArcView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ThreeArcView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initAttrs(context, attrs);
        initVariable();
    }

    private void initAttrs(Context context, AttributeSet attrs) {
        TypedArray typeArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ThreeArcView, 0, 0);
        mArcStrokeWidth = typeArray.getDimension(R.styleable.ThreeArcView_ts_strokeWidth, dp2px(context, 20));
        // 圓環背景顏色
        mBackgroundArcColor = typeArray.getColor(R.styleable.ThreeArcView_ts_bgArcColor, 0xFFFFFFFF);
        // 圓環顏色
        mOutsideArcColor = typeArray.getColor(R.styleable.ThreeArcView_ts_outsideBgColor, 0xFFFFFFFF);
        mMiddleArcColor = typeArray.getColor(R.styleable.ThreeArcView_ts_middleBgColor, 0xFFFFFFFF);
        mInsideArcColor = typeArray.getColor(R.styleable.ThreeArcView_ts_insideBgColor, 0xFFFFFFFF);
        // 圓進度
        mOutsideProgress = typeArray.getFloat(R.styleable.ThreeArcView_ts_outsideProgress, 0f);
        mMiddleProgress = typeArray.getFloat(R.styleable.ThreeArcView_ts_middleProgress, 0f);
        mInsideProgress = typeArray.getFloat(R.styleable.ThreeArcView_ts_insideProgress, 0f);
        // 圓環偏移值
        mArcOffset = typeArray.getDimension(R.styleable.ThreeArcView_ts_radiusOffset, dp2px(context, 20));
        typeArray.recycle();

        // 偏移值不能小於畫筆寬度的一半,不然會發生覆蓋
        if (mArcOffset < mArcStrokeWidth / 2){
            mArcOffset = mArcStrokeWidth / 2;
        }
    }

    private void initVariable() {
        //背景圓弧畫筆設置
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(mArcStrokeWidth);
        mPaint.setStrokeCap(Paint.Cap.ROUND);//開啓顯示邊緣爲圓形
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // 分別獲取指望的寬度和高度,並取其中較小的尺寸做爲該控件的寬和高
        int measureWidth = MeasureSpec.getSize(widthMeasureSpec);
        int measureHeight = MeasureSpec.getSize(heightMeasureSpec);
        //裁剪出一個 (寬:高) = (2:1) 的矩形
        int finalWidth = measureWidth;
        int finalHeight = measureHeight;
        if (measureWidth >= measureHeight * 2) {
            finalWidth = measureHeight * 2;
        } else {
            finalHeight = measureWidth / 2;
        }
        setMeasuredDimension(finalWidth, finalHeight);
    }

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

        // 初始半徑
        float originalRadius = getWidth() * .5f;
        // 畫筆半寬
        float halfArcStokeWidth = mArcStrokeWidth * .5f;

        // 外圓環半徑=初始半徑-畫筆半寬
        float outSideArcRadius = originalRadius - halfArcStokeWidth;
        drawArc(canvas, mOutsideProgress, mOutsideArcColor, outSideArcRadius);

        // 中圓環半徑=外圓的半徑-圓環偏移值-畫筆半寬
        float middleArcRadius = outSideArcRadius - mArcOffset - halfArcStokeWidth;
        drawArc(canvas, mMiddleProgress, mMiddleArcColor, middleArcRadius);

        // 內圓環半徑=中圓的半徑-圓環偏移值-畫筆半寬
        float insideArcRadius = middleArcRadius - mArcOffset - halfArcStokeWidth;
        drawArc(canvas, mInsideProgress, mInsideArcColor, insideArcRadius);
    }

    /** * 繪製圓弧 * @param canvas * @param progress 進度 * @param color 進度顏色 * @param radius 圓弧半徑 */
    private void drawArc(Canvas canvas, float progress, int color, float radius){
        // 圓心
        mXCenter = getWidth() / 2;
        mYCenter = getHeight() ;

        mPaint.setColor(mBackgroundArcColor);
        // 構造邊界矩形
        RectF oval = new RectF();
        oval.left = (mXCenter - radius);
        oval.top = (mYCenter - radius);
        oval.right = mXCenter + radius;
        oval.bottom = radius * 2 + (mYCenter - radius);
        //繪製圓弧背景
        canvas.drawArc(oval, -180, 180, false, mPaint);

        //繪製圓弧進度
        float showDegree = progress / 100 * 180;
        mPaint.setColor(color);
        canvas.drawArc(oval, -180, showDegree, false, mPaint);
    }

    private void setOutSideProgress(float progress){
        this.mOutsideProgress = progress;
        postInvalidate();
    }

    private void setMiddleProgress(float progress){
        this.mMiddleProgress = progress;
        postInvalidate();
    }

    private void setInsideProgress(float progress){
        this.mInsideProgress = progress;
        postInvalidate();
    }

    public void setProgress(float outSideProgress, float middleProgress, float insideProgress) {
        mOutsideProgress = outSideProgress;
        mMiddleProgress = middleProgress;
        mInsideProgress = insideProgress;
        postInvalidate();
    }

    public int dp2px(Context context, float dipValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dipValue * scale + 0.5f);
    }

    public int sp2px(Context context, float spValue) {
        final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (spValue * fontScale + 0.5f);
    }

    public int px2sp(Context context, float pxValue) {
        final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (pxValue / fontScale + 0.5f);
    }
}
複製代碼

styes.xmlrest

<declare-styleable name="ThreeArcView">
    <!-- 畫筆寬度 -->
    <attr name="ts_strokeWidth" format="dimension" />
    <!-- 圓弧背景色 -->
    <attr name="ts_bgArcColor" format="color" />
    <!-- 外圓進度顏色 -->
    <attr name="ts_outsideBgColor" format="color" />
    <!-- 中圓進度顏色 -->
    <attr name="ts_middleBgColor" format="color" />
    <!-- 內圓進度顏色 -->
    <attr name="ts_insideBgColor" format="color" />
    <!-- 外圓進度 -->
    <attr name="ts_outsideProgress" format="float" />
    <!-- 中圓進度 -->
    <attr name="ts_middleProgress" format="float" />
    <!-- 內圓進度 -->
    <attr name="ts_insideProgress" format="float" />
    <!-- 圓偏移值 -->
    <attr name="ts_radiusOffset" format="dimension" />
</declare-styleable>
複製代碼

OK,本文到此結束,若發現問題,歡迎一塊兒留言一塊兒探討,感謝~code

相關文章
相關標籤/搜索