Android自定义View实践 空气质量检测 pm2.5

直接先看效果图
在这里插入图片描述

自定义气体检测视图我们先整理下需要做的的事情

  1. 画五个圆弧
  2. 每个圆弧上再通过具体的数据绘制一定角度的圆弧
  3. 甲醛那个进度条比较特殊,一头平一头椭圆该怎么实现?
  4. 文字的绘制

明白了需求我们开搞

画背景圆弧很简单canvas.drawArc 参数分别是圆弧所在的矩形范围、圆弧绘制的其实角度、圆弧划过的角度,是否扫过圆心

  public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint) {
        throw new RuntimeException("Stub!");
    }

在这里插入图片描述
好,那我们先定义加个正方形的宽度 为了适配我引用了AutoSize框架,你可以不用理会。

   float[] whiteCircle  = new float[]{AutoSizeUtils.dp2px(getContext(), 192),
                AutoSizeUtils.dp2px(getContext(), 208),
                AutoSizeUtils.dp2px(getContext(), 226), AutoSizeUtils.dp2px(getContext(), 244)};

宽度有了我们是不是要计算每个正方形放在屏幕中心是的位置也就是第一个参数RectF的构造
首先获取屏幕的宽高

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mMeasureWidth = getMeasuredWidth();
        mMeasureHeight = getMeasuredHeight();
    }

然后构造这个RectF

  public RectF(float left, float top, float right, float bottom) {
        throw new RuntimeException("Stub!");
    }

在这里插入图片描述
看懂了吧 上下左右分别对应的长度如图所示,再看不懂就自己消化下!

那么得出

float left = mMeasureWidth / 2 - 正方形宽 / 2
float top = mMeasureHeight / 2 - 正方形宽 / 2
float right = mMeasureWidth / 2 + 正方形宽 / 2
float left = mMeasureHeight / 2 + 正方形宽 / 2

RectF rect = new RectF(mMeasureWidth / 2 - progressR / 2,
                        mMeasureHeight / 2 - progressR / 2,
                        mMeasureWidth / 2 + progressR / 2,
                        mMeasureHeight / 2 + progressR / 2);

第一个参数搞定,起始角度90度 划过的角度270。不划过圆心。画笔

  Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
  paint.setStyle(Paint.Style.STROKE);/描边模式
  paint.setColor(Color.parseColor("#958280"));//灰色圆弧
  paint.setStrokeWidth(grayPaintWidth);//圆弧宽度
  paint.setStrokeCap(Paint.Cap.BUTT);//两头方形
  canvas.drawArc(rect, 90, 270, false, paint);//最终绘制好一个圆弧

那么接下来开始绘制进度条,除了甲醛的其他都好办,改变画笔颜色按角度绘制就行了

//绘制白色圆
                paint.setColor(Color.WHITE);
                paint.setStrokeWidth(whitePaintWidth);
                float arc = 270 * gasData.getProgress() / gasData.getMax();
                if(arc>270) arc = 270;
                paint.setColor(Color.WHITE);
                paint.setStrokeWidth(4);
                canvas.drawArc(rect, 90, arc, false, paint);

甲醛这个该咋整?

画笔有个属性可以设置椭圆或者方形,但是貌似没有一边圆弧一边方形的,那就来个投机取巧。先画一个方形的小块再接上圆角的。类似这样你懂的吧。
在这里插入图片描述

还有个绿点的绘制。这里我们要用到一个公式,已知圆心、半径、角度求圆上点的坐标

            float radius = pmProgressWidth / 2;
            float x = (float) (centerX + Math.cos(radian) * radius);
            float y = (float) (centerY + Math.sin(radian) * radius);
//arc角度大于3我们画 如果小于3直接画个绿点即可
if (arc >= 3) {
            paint.setStrokeCap(Paint.Cap.SQUARE);
            canvas.drawArc(rect, 93, 1, false, paint);
            paint.setStrokeCap(Paint.Cap.ROUND);
            canvas.drawArc(rect, 94, arc, false, paint);
            float radian = (float) Math.toRadians(arc + 93);
            float centerX = rect.centerX();
            float centerY = rect.centerY();
            float radius = pmProgressWidth / 2;
            float x = (float) (centerX + Math.cos(radian) * radius);
            float y = (float) (centerY + Math.sin(radian) * radius);
            paint.setColor(Color.RED);
            RectF rectDot = new RectF(x - pmPaintWidth / 2, y - pmPaintWidth / 2, x + pmPaintWidth / 2, y + pmPaintWidth / 2);
            //绘制甲醛图片背景
            canvas.drawBitmap(dotImg, null, rectDot, null);
        } else {
            //绘制甲醛图片背景
            canvas.drawBitmap(dotImg, rect.centerX() - pmPaintWidth / 2, rect.bottom - pmPaintWidth / 2, null);
        }

接下来画文字

这里借助Path这类,在路径上画文字

//绘制甲醛文字
        Path path = new Path();
        path.moveTo(mMeasureWidth / 2 + 15, rect.bottom + 10);//移动到圆形下方
        path.lineTo(mMeasureWidth, rect.bottom);//水平的路径
        textPaint.setTextSize(AutoSizeUtils.dp2px(getContext(), 14));
        canvas.drawTextOnPath(gasData.getGasName(), path, 0, 0, textPaint);//画文字

大功告成,最后贴上完整代码。你等的不就是这么。。

GasView.java

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

import com.iisfree.smarthome.R;
import com.iisfree.smarthome.model.bean.DeviceBean;

import java.util.ArrayList;
import java.util.List;

import me.jessyan.autosize.utils.AutoSizeUtils;

public class GasView extends View {
    private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
    private Paint textPaint;
    private List<GasData> gasDataList;
    private int mMeasureHeight;
    private int mMeasureWidth;
    private float whitePaintWidth;//白色圆弧宽度
    private float grayPaintWidth;//灰色圆弧宽度
    private float pmPaintWidth;//红色圆点宽度
    private float bgWidth;//背景图大小
    private float bgProgressWidth;//进度图大小
    private Bitmap imgBg;
    private Bitmap imgProgressBg;
    private Bitmap dotImg;
    private float[] whiteCircle;
    private float pmProgressWidth;//彩色进度条的半径


    public GasView(Context context) {
        super(context);
        initPaint(context);
    }

    public GasView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initPaint(context);
    }

    public GasView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaint(context);
    }

    private void initPaint(Context context) {
        gasDataList = new ArrayList<>();
        paint.setStyle(Paint.Style.STROKE);
        paint.setColor(Color.WHITE);
        textPaint = new Paint();
        textPaint.setAntiAlias(true);
        textPaint.setTextSize(AutoSizeUtils.dp2px(getContext(), 8));
        textPaint.setColor(Color.parseColor("#666666"));
        textPaint.setStyle(Paint.Style.FILL);
        whitePaintWidth = AutoSizeUtils.dp2px(getContext(), 2);//白色圆弧宽度
        grayPaintWidth = AutoSizeUtils.dp2px(getContext(), 1);//灰色圆弧宽度
        pmPaintWidth = AutoSizeUtils.dp2px(getContext(), 13);//红色圆点宽度
        bgWidth = AutoSizeUtils.dp2px(getContext(), 136);//红色圆点宽度
        bgProgressWidth = AutoSizeUtils.dp2px(getContext(), 168);//红色圆点宽度
        pmProgressWidth = AutoSizeUtils.dp2px(getContext(), 157);

        whiteCircle = new float[]{AutoSizeUtils.dp2px(getContext(), 192),
                AutoSizeUtils.dp2px(getContext(), 208),
                AutoSizeUtils.dp2px(getContext(), 226), AutoSizeUtils.dp2px(getContext(), 244)};

        //绘制背景图片
        BitmapFactory.Options option = new BitmapFactory.Options();
        option.inScaled = false;
        option.inPreferredConfig = Bitmap.Config.ARGB_8888;
        imgBg = BitmapFactory.decodeResource(getResources(), R.mipmap.air_img_03, option).copy(Bitmap.Config.ARGB_8888, true);
        imgProgressBg = BitmapFactory.decodeResource(getResources(), R.mipmap.air_img_02, option).copy(Bitmap.Config.ARGB_8888, true);
        dotImg = BitmapFactory.decodeResource(getResources(), R.mipmap.air_img_01, option).copy(Bitmap.Config.ARGB_8888, true);
    }


    public void freshData(List<DeviceBean.SrDevice.SubAirSensor> gasDataList) {
        this.gasDataList.clear();
        this.gasDataList.addAll(gasDataList);
        postInvalidate();
    }

    private static final String TAG = "GasView";

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (this.gasDataList == null || this.gasDataList.size() == 0) return;
        paint.setStrokeCap(Paint.Cap.BUTT);
        RectF rectF = new RectF(mMeasureWidth / 2 - bgWidth / 2,
                mMeasureHeight / 2 - bgWidth / 2,
                mMeasureWidth / 2 + bgWidth / 2,
                mMeasureHeight / 2 + bgWidth / 2);
        //绘制背景图片
        canvas.drawBitmap(imgBg, null, rectF, null);

        RectF recBg = new RectF(mMeasureWidth / 2 - bgProgressWidth / 2,
                mMeasureHeight / 2 - bgProgressWidth / 2,
                mMeasureWidth / 2 + bgProgressWidth / 2,
                mMeasureHeight / 2 + bgProgressWidth / 2);
        //绘制甲醛图片背景
        canvas.drawBitmap(imgProgressBg, null, recBg, null);

        for (GasData gasData : gasDataList) {
            float progressR = 0;
            switch (gasData.getSensorType()) {
                case 0: {
                    drawCh2o(canvas, gasData);
                }
                break;
                case 1: {
                    progressR = whiteCircle[0];
                }
                break;
                case 2: {
                    progressR = whiteCircle[1];
                }
                break;
                case 3: {
                    progressR = whiteCircle[2];
                }
                break;
                case 4: {
                    progressR = whiteCircle[3];
                }
                break;
            }
            if (gasData.getSensorType() != 0) {
                RectF rect = new RectF(mMeasureWidth / 2 - progressR / 2,
                        mMeasureHeight / 2 - progressR / 2,
                        mMeasureWidth / 2 + progressR / 2,
                        mMeasureHeight / 2 + progressR / 2);
                //绘制灰色圆
                paint.setColor(Color.parseColor("#958280"));
                paint.setStrokeWidth(grayPaintWidth);
                canvas.drawArc(rect, 90, 270, false, paint);
                //绘制白色圆
                paint.setColor(Color.WHITE);
                paint.setStrokeWidth(whitePaintWidth);

                float arc = 270 * gasData.getProgress() / gasData.getMax();
                if(arc>270) arc = 270;
                paint.setColor(Color.WHITE);
                paint.setStrokeWidth(4);
                canvas.drawArc(rect, 90, arc, false, paint);

                Path path = new Path();
                path.moveTo(mMeasureWidth / 2 + 20, rect.bottom + 5);
                path.lineTo(mMeasureWidth, rect.bottom);
                textPaint.setTextSize(AutoSizeUtils.dp2px(getContext(), 8));
                canvas.drawTextOnPath(gasData.getGasName(), path, 0, 0, textPaint);
            }

        }

    }

    private void drawCh2o(Canvas canvas, GasData gasData) {
        //绘制甲醛
        float arc = 270 * gasData.getProgress() / gasData.getMax();
        if(arc>270) arc = 270;
        paint.setColor(Color.WHITE);
        paint.setStrokeWidth(pmPaintWidth);

        RectF rect = new RectF(mMeasureWidth / 2 - pmProgressWidth / 2,
                mMeasureHeight / 2 - pmProgressWidth / 2,
                mMeasureWidth / 2 + pmProgressWidth / 2,
                mMeasureHeight / 2 + pmProgressWidth / 2);
        Log.d(TAG, "onDraw角度: " + arc);
        if (arc >= 3) {
            paint.setStrokeCap(Paint.Cap.SQUARE);
            canvas.drawArc(rect, 93, 1, false, paint);
            paint.setStrokeCap(Paint.Cap.ROUND);
            canvas.drawArc(rect, 94, arc, false, paint);
            float radian = (float) Math.toRadians(arc + 93);
            float centerX = rect.centerX();
            float centerY = rect.centerY();
            float radius = pmProgressWidth / 2;
            float x = (float) (centerX + Math.cos(radian) * radius);
            float y = (float) (centerY + Math.sin(radian) * radius);
            paint.setColor(Color.RED);
            RectF rectDot = new RectF(x - pmPaintWidth / 2, y - pmPaintWidth / 2, x + pmPaintWidth / 2, y + pmPaintWidth / 2);
            //绘制甲醛图片背景
            canvas.drawBitmap(dotImg, null, rectDot, null);
        } else {
            //绘制甲醛图片背景
            canvas.drawBitmap(dotImg, rect.centerX() - pmPaintWidth / 2, rect.bottom - pmPaintWidth / 2, null);
        }

        //绘制甲醛文字
        Path path = new Path();
        path.moveTo(mMeasureWidth / 2 + 15, rect.bottom + 10);
        path.lineTo(mMeasureWidth, rect.bottom);
        textPaint.setTextSize(AutoSizeUtils.dp2px(getContext(), 14));
        canvas.drawTextOnPath(gasData.getGasName(), path, 0, 0, textPaint);
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mMeasureWidth = getMeasuredWidth();
        mMeasureHeight = getMeasuredHeight();
    }
}

GasData.java
自己的数据实体实现这个接口即可

public interface GasData {
    String getGasName();//气体名字
    int getMax();//气体最大值
    int getProgress();//当前气体浓度
    int getSensorType();//气体类型
}

有什么不懂得联系我 QQ910689331

发布了53 篇原创文章 · 获赞 17 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/qq910689331/article/details/104577808