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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章