自定义仪表盘笔记

来源:互联网 发布:软件系统设计实例 编辑:程序博客网 时间:2024/06/06 02:48

参考自:http://blog.csdn.net/vv_bug/article/details/71694562

1.新建DashboardView类继承View,一些准备工作

public class DashboardView extends View{    //弧形开始的角度    private static final int startAngle = 180;    //弧面所跨的弧度    private static final int sweepAngle = 180;    //里面数字的单位    private static final String unint = "mmhg";    //每隔多少画一个刻度    private static final int angPre = 2;    //总刻度    private static final int totalDial = 90;    //进度条的底色    private static final int PROGRESS_COLOR = 0x55000000;    //画普通的线用的笔    private Paint linePaint;    //画文字用的笔    private Paint textPaint;    //画进度条用的笔    private Paint progressPaint;    //里面半圆的半径    private int innerRadius = dp2px(100);    //最内层的padding    private int innerPadding = dp2px(6);    //外两层的padding    private int outerPadding = dp2px(10);    //进度条的宽度    private int progressLineW = dp2px(8);    //最里面跟最外面的线的宽度    private int innerLineWidth = dp2px(1);    //刻度线的宽度    private int outerLineWidth = dp2px(2);    //刻度线的高度    private int outerLineHeight = dp2px(10);    //文字的size    private float textSize = sp2px(18);    //单位文字的size    private float textSizeUnit = sp2px(13);    //当前进度    private float currProgress = 0.5f;    //起始值    private float start = 0;    //最终值    private float end = 150;    public DashboardView(Context context) {        this(context, null);    }    public DashboardView(Context context, @Nullable AttributeSet attrs) {        this(context, attrs, 0);    }    public DashboardView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        obtainAttributes(attrs);    }    /**     * 获取自定义的属性     */    private void obtainAttributes(AttributeSet attrs) {        init();    }    /**     * 初始化     */    private void init() {        linePaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);        linePaint.setStyle(Paint.Style.STROKE);        linePaint.setColor(Color.WHITE);        progressPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);        progressPaint.setStyle(Paint.Style.STROKE);        progressPaint.setColor(Color.WHITE);        progressPaint.setStrokeWidth(progressLineW);        progressPaint.setStrokeCap(Paint.Cap.ROUND);        textPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);        textPaint.setStyle(Paint.Style.STROKE);        textPaint.setColor(Color.WHITE);        textPaint.setTextSize(textSize);    }}

然后先是onMeasure

    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        //最里面圆的半径计算        //半径=控件的宽度/2-最里层的padding-刻度线的高度-里第二层的padding-进度条的宽度-第二层padding-最外层线宽度        innerRadius = getMeasuredWidth() / 2 - innerPadding - outerLineHeight -                outerPadding - progressLineW - outerPadding - innerLineWidth;        //因为view的设置的高度是wrap_content,所以我们要重新计算高度        //高度=刻度线的高度+padding+最里面一层的半径+padding+进度条宽度+padding+最外层线宽度        int height = (outerLineHeight + innerPadding + innerRadius + outerPadding + progressLineW + outerPadding + innerLineWidth);        //重新生成高度        heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);        super.onMeasure(widthMeasureSpec, heightMeasureSpec);    }

然后是onDraw

@Overrideprotected void onDraw(Canvas canvas) {    drawInnerArc(canvas);  }

画最里层的圆弧
*先定义一个矩形,矩形其实为一个圆的大小
*画圆弧 180度开始 到 360度结束 这样就有一个半圆

private void drawInnerArc(Canvas canvas) {    //定义一个矩形区域    RectF rectF = new RectF();    int width = getWidth();    //矩形的top为 padding + 刻度线的高度+padding+进度条宽度 +padding +最外层弧线线宽度    //left为控件的宽度/2 - 半圆半径    int top = outerLineHeight + innerPadding + innerLineWidth + outerPadding * 2 + progressLineW;    rectF.set(width / 2 - innerRadius, top, width / 2 + innerRadius, top + innerRadius * 2);    linePaint.setStrokeWidth(innerLineWidth);    //弧线的起始位置为180度的位置,扫过的弧度为180    canvas.drawArc(rectF, startAngle, sweepAngle, false, linePaint);}

看下效果
这里写图片描述

然后画刻度
*用角度的方法来画,利用三角函数算出刻度两端的点的值
*算出来的以圆弧的中心点的坐标系,真正的坐标系为左上角,所以还要加上getWidth()/2或者getHeight()

private void drawDial(int startAngle, int allAngle, int dialCount, int per, int longLength, int shortLength, int radius, Canvas canvas) {    linePaint.setStrokeWidth(outerLineWidth);    int length;    int angle;    //根据需要显示的刻度总个数遍历    for (int i = 0; i <= dialCount; i++) {        //每一个刻度对应的起始角度为180度+(总度数/个数)*对应刻度的位置        angle = (int) ((allAngle) / (dialCount * 1f) * i) + startAngle;        //线条的起始点位置        int[] startP;        //线条的end点的位置        int[] endP;        //当i%per==0,每一个需要显示短刻度的时候(因为设计稿第一个为短的刻度条)        if (i % per == 0) {            //短刻度条的长度为长刻度条的一半            length = shortLength;            //获取刻度条起始点位置            startP = getPointFromAngleAndRadius(angle, radius - length);            endP = getPointFromAngleAndRadius(angle, radius - length * 2);        } else {            length = longLength;            startP = getPointFromAngleAndRadius(angle, radius);            endP = getPointFromAngleAndRadius(angle, radius - length);        }        //画出对应的刻度条        canvas.drawLine(startP[0], startP[1], endP[0], endP[1], linePaint);    }}/** * 根据刻度条相应的角度算出点位置 * @param angle * @param radius * @return */private int[] getPointFromAngleAndRadius(int angle, int radius) {    //根据三角函数公式可以知道,横坐标值为(刻度条+innnerradius)也就是刻度条对应圆的半径    //乘以一个cos(angle),因为我们是以(getWidth() / 2,控件的高度)位置建的坐标系    //而真正的坐标系的位置为控件左上角,所以算出的值后需要+getWidth() / 2或者getHeight()    double x = radius * Math.cos(angle * Math.PI / 180) + getWidth() / 2;    double y = radius * Math.sin(angle * Math.PI / 180) + getHeight();    return new int[]{(int) x, (int) y};}

看下效果
这里写图片描述

接下来画文字
主要是确定文字的位置

private void drawText(Canvas canvas) {    //当前文字对应的值为(0+(150-0)*当前进度)    String currText = String.valueOf((int) (start + (end - start) * currProgress));    //因为数字字体大而单位数字小    textPaint.setTextSize(textSize);    //测量数字文字对应的长度    float numWidth = textPaint.measureText(currText);    //重新设置笔的size    textPaint.setTextSize(textSizeUnit);    //为了获取单位文字的高度    Rect rect = new Rect();    //获取单位文字的最小矩形范围    textPaint.getTextBounds(unint, 0, unint.length(), rect);    //单位文字的宽度    float unitWidth = textPaint.measureText(unint);    //从新设置笔的大小    textPaint.setTextSize(textSize);    //文字的basex为(控件的宽度/2-(数字文字的长度+单位文字的长度)/2)    float baseX = getWidth()/2 - (unitWidth + numWidth) / 2;    //文字的centery为(最外层线的宽度+padding+进度条宽度+padding+padding+最里面半圆半径的一半)    float centerY = innerLineWidth + outerPadding + progressLineW + outerPadding + outerLineHeight + innerPadding + innerRadius / 2;    //(主要解决文字在半圆的中心文字)根据centery算出文字的basey    float baseY = centerY - (textPaint.ascent() + textPaint.descent()) / 2;    //设置数字文字为粗体    textPaint.setFakeBoldText(true);    //画出数字文字    canvas.drawText(currText + "", baseX, baseY, textPaint);    //重新设置画笔    textPaint.setTextSize(textSizeUnit);    textPaint.setFakeBoldText(false);    //画出单位文字(跟数字文字底部有一个偏移量所以basey-了一个(单位文字的高度的1/6))    canvas.drawText(unint, baseX + numWidth + dp2px(1), baseY - rect.height() / 6, textPaint);}

看下效果
这里写图片描述

画出最外面的弧线跟进度条
*画弧的时候,弧的画笔宽度的一半会和矩形相切

private void drawOuterStaticLine(Canvas canvas) {    //最外层的弧线    RectF rectF1 = new RectF();    int width = getWidth();    rectF1.set(innerLineWidth, innerLineWidth, width - innerLineWidth, getHeight() * 2 - innerLineWidth);    linePaint.setStrokeWidth(innerLineWidth);    canvas.drawArc(rectF1, startAngle, sweepAngle, false, linePaint);    //静态的进度条    progressPaint.setColor(PROGRESS_COLOR);    RectF rectF2 = new RectF();    rectF2.set(innerLineWidth + outerPadding + outerPadding / 2, innerLineWidth + outerPadding + outerPadding / 2,            width - (innerLineWidth + outerPadding + outerPadding / 2), getHeight() * 2 - ((innerLineWidth + outerPadding + outerPadding / 2)));    canvas.drawArc(rectF2, startAngle, sweepAngle, false, progressPaint);}

看下效果
这里写图片描述

最后就是设置进度的方法了

public void setProgress(float progress){    if (currProgress == progress) {        return;    }    this.currProgress = progress;    if (Looper.myLooper() == Looper.getMainLooper()) {        invalidate();    } else {        postInvalidate();    }}

测试

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"              android:id="@+id/activity_main"              android:layout_width="match_parent"              android:layout_height="match_parent"              android:background="#a7ff0000"              android:gravity="center_horizontal"              android:orientation="vertical">        <com.lzz.dashboard.DashboardView            android:id="@+id/dashboardView"            android:background="@color/colorAccent"            android:layout_marginTop="40dp"            android:layout_marginLeft="40dp"            android:layout_marginRight="40dp"            android:layout_width="match_parent"            android:layout_height="wrap_content"/></LinearLayout>
public class MainActivity extends AppCompatActivity {    DashboardView dashboardView;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        dashboardView = (DashboardView) findViewById(R.id.dashboardView);        startAni();    }    private void startAni() {        //对应dashboardView中的setProgress()方法        ObjectAnimator a= ObjectAnimator.ofFloat(dashboardView,"progress",0f,1f);        a.setInterpolator(new AccelerateDecelerateInterpolator());        a.setDuration(3000);        a.setRepeatCount(ValueAnimator.INFINITE);        a.setRepeatMode(ValueAnimator.REVERSE);        a.start();    }}

这里写图片描述

原创粉丝点击