直接先看效果图

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

  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的构造
首先获取屏幕的宽高

    @Overrideprotected 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 + 正方形宽 / 2RectF 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";@Overrideprotected 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);}@Overrideprotected 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

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

  1. 基于stm32的空气质量检测净化系统,检测温湿度和pm10和 pm2.5,资料包含(设计文档,源码,pcb电路)。

    基于stm32的空气质量检测净化系统,检测温湿度和pm10和 pm2.5,资料包含(设计文档,源码,pcb电路).

  2. Android 系统(201)---Android 自定义View实战系列 :时间轴

    Android 自定义View实战系列 :时间轴 Android开发中,时间轴的 UI需求非常常见,如下图: 本文将结合 自定义View & RecyclerView的知识,手把手教你实现该常 ...

  3. Android自定义view之事件传递机制

    Android自定义view之事件传递机制 在上一篇文章<Android自定义view之measure.layout.draw三大流程>中,我们探讨了一下view的显示过程.不太熟悉的同学 ...

  4. Android自定义View实现方位刻度尺(类似于吃鸡手游)

    Android自定义View实现方位刻度尺(类似于吃鸡手游) 先上效果图 gif可能看不清,我下面放几张图片 原理解析 首先,我们应该把看得到的内容从上至下分成三部分:最上面的文字.中间的竖线和最下面 ...

  5. DIY 空气质量检测表

    DIY 空气质量检测表 前几天逛淘宝看到有空气颗粒物浓度测量的传感器,直接是 3.3V TTL 电压串口输出的,也不贵,也就 100 多一点.觉得挺好就买了个,这两天自己捣鼓了个小程序,搞了个软件界面 ...

  6. Android自定义View之画圆环(手把手教你如何一步步画圆环)

    关于自定义View: 好了,吐槽时间到.自定义view是Android开发知识体系中的重点,也是难点.好多小伙伴(也包括我)之前对自定义view也是似懂非懂.那种感觉老难受了.因此作为社会主义好青年, ...

  7. Android 自定义View之随机数验证码(仿写鸿洋)

    前言 本文面向自定义view新手,但是希望你最好有一定的理论知识,或基础概念,有的地方可能会一笔带过并不会细讲,细讲篇幅就太长了. 本文仿写自鸿洋的自定义View (一),尽管过去了将近快7年之久,我 ...

  8. stm32毕业设计 空气质量检测系统

    文章目录 1 简介 2 系统设计概述 3 系统总体方案 4 硬件设计方案 4.1 stm32 主控 4.2 温度采集模块 4.3 甲醛浓度检测模块 4.4 PM2. 5 浓度检测模块 4.5 液晶显示 ...

  9. Android自定义View——实现理财类APP七日年化收益折线图效果

    这段时间的自定义View学习,学会了绘制柱状图.绘制折线图.绘制进度控件,那我们今天就来聊聊另外一种自定义的View,这就是我们常见的七日年化收益折线图效果.先看看长什么样. 这就是效果图了,元素相对 ...

最新文章

  1. linux C 多线程编程
  2. Windows Server 2008 R2 Beta VHD镜像文件发布
  3. 数据库自动收缩带来的严重问题
  4. ASP.NET程序中常用代码汇总(四)
  5. 策略模式学习三---总结
  6. ABP入门系列(5)——展现层实现增删改查
  7. 【CH5105】Cookies
  8. FileZilla软件下载使用简易教程
  9. python beautifulsoup报错bs4 FeatureNotFound Couldnot find a tree builder with the features
  10. LeetCode 945. 使数组唯一的最小增量
  11. 视频客观质量评价工具:MSU Video Quality Measurement Tool
  12. unity 编辑器 混合使用固定布局和自动布局(二)
  13. putty screen 快捷键
  14. cherry 键盘失灵记录
  15. premiere pr 裁剪视频音频
  16. Baklib知识库-企业知识库管理平台
  17. 人民币大写的正确写法(开票据事项)
  18. 使用JiaoZiVideoPlayer播放视频方向横过来出现的问题
  19. MySQL基本概念和正确发音(表、列、行、注解等讲解)
  20. 当docker pull mysql时,一直Waiting,很多等待,报:error pulling image configuration

热门文章

  1. 五 Deepin安装java
  2. Go语言中如何进行测试
  3. OKR 和 KPI 的适用场景
  4. java反射 基本知识
  5. IOS开发网络第一天之06线程之间的通信
  6. x264代码剖析(十四):核心算法之宏块编码函数x264_macroblock_encode()
  7. android:使用audiotrack 类播放wav文件
  8. 线程调度 java_Java多线程--线程的调度
  9. ubuntu 使用ccache加快linux内核编译速度
  10. 北京计算机科学与技术学院,计算机科学与技术学院