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

相关文章
相关标签/搜索