Android自定义view流程

Android自定义view流程,主要目的是总结实现过程中的思路以及一些需要注意的地方。 首先,我们先来看一张效果图:
在这里插入图片描述

实现逻辑

  • 重新指定View宽高
  • 绘制外圆圆弧背景及进度
  • 绘制中圆圆弧背景及进度
  • 绘制内圆圆弧背景及进度

知识点

onMeasure

  • 用于测量View的大小。创建时View无需测量,当将这个View放入一个容器(父控件)时候才需要测量,而测量方法由父控件调用。当控件的父控件要放置该控件的时候,父控件会调用子控件的onMeasure方法确定子控件需要的空间大小,然后传入widthMeasureSpec和heightMeasureSpec来告诉子控件可获得的空间大小,子控件通过这两个参数就可以测量自身的宽高了。

setMeasuredDimension

  • 用于重新设置View宽高

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

  • 绘制以oval为边界的圆弧

onDraw

  • 用来确定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.java

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.xml

<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,本文到此结束~

最后这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶资料》,帮助大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。

由于文章内容比较多,篇幅有限,资料已经被整理成了PDF文档,有需要《Android八大模块进阶资料》完整文档的可以加微信 即可免费领取!

《Android八大模块进阶笔记》

在这里插入图片描述

相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

一、源码解析合集

在这里插入图片描述

二、开源框架合集

在这里插入图片描述

同时这里还搭建了一个基于chatGPT的微信群聊机器人,24小时为大家解答疑难技术问题

图片

猜你喜欢

转载自blog.csdn.net/huahaiyi/article/details/132581484