欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android随笔之简单仪表盘

程序员文章站 2022-05-26 22:01:57
...

import android.content.Context;

import android.graphics.Canvas;

import android.graphics.Color;

import android.graphics.Paint;

import android.graphics.Path;

import android.graphics.Rect;

import android.graphics.RectF;

import android.graphics.Typeface;

import android.os.Handler;

import android.support.annotation.Nullable;

import android.util.AttributeSet;

import android.util.DisplayMetrics;

import android.view.View;

import baidertrs.zhijienet.util.DensityUtils;

/**

* Created by RSW on 2018/4/23.

* baidertrs.zhijienet.ui.view.dashboard

*/

public class PanelViewextends Viewimplements Runnable {

private int mWidth;

private int mHeight;

private int screenWidth;//屏幕宽

    private int screenHeight;//屏幕高

    private int mDensityDpi;//屏幕dpi

    private int raduis;//半径

    private int pointX, pointY;//圆心

    private float mStartAngle  =180f;// 起始角度

    private float mSweepAngle  =180f;// 绘制角度

    private float animprogress =0.0f;// 指针动画进度

    Context mContext;

float zhizhenValue =0f;

float jiaodu      =0f;

float jiaodu2      =0f;

private float  mPortion =14;// 一个mSection等分份数

    private float  mSection =7;// 值域(mMax-mMin)等分份数

    private Handler handler  =new Handler();// 用于延时更新,实现动画

// 第一个参数表示角度;负数表示左边球旋转的角度,正数表示右边球旋转的角度

// +angle表示右侧球偏离最大的角度

// -angle表示左侧球偏离最大的角度

// 第二个参数表示方向;-1表示从右往左摆动,1表示从左往右摆动

    private float t =15f;//时间;可以用来控制速率,t越小,摆钟越慢;t越大,摆钟越快

    private float cosO;//cosθ,是个固定值

    private float gr2;//2gr,是个固定值

    private float  angle  =0.8f;//最大旋转角度

    private float[] degree =new float[]{angle, -1};

int flag =0;

Paint bigArcPaint;

Paint keduPaint1, keduPaint2;

Paint outCirclePaint;

Paint numberPaint;

Paint xuxianPaint1, xuxianPaint2;

Paint keduArcPaint1, keduArcPaint2;

Paint smallArcPaint;

Paint zhizhenPaint;

RectF bigArcRectF;

RectF keduRectF;

RectF xuxianRectF;

RectF keduArcRectF;

RectF smallArcRectF;

public float getZhizhenValue() {

return zhizhenValue;

}

public void setZhizhenValue(float zhizhenValue) {

//        if (Build.VERSION.SDK_INT > 23) {

//            t = 7.5f;

//        } else {

//            t = 15f;

//        }

        System.out.println("zhizhenValue=============="+zhizhenValue);

flag =0;

this.zhizhenValue = zhizhenValue;

animprogress =0.0f;

invalidate();

}

public PanelView(Context context) {

this(context,null);

calCosOAnd2gr();

}

public PanelView(Context context, @Nullable AttributeSet attrs) {

this(context, attrs,0);

calCosOAnd2gr();

}

public PanelView(Context context, @Nullable AttributeSet attrs,int defStyleAttr) {

super(context, attrs, defStyleAttr);

calCosOAnd2gr();

this.mContext = context;

// TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PanelView, defStyleAttr, 0);

//获取屏幕宽高 和 屏幕密度dpi

        DisplayMetrics displayMetrics = getResources().getDisplayMetrics();

screenWidth = displayMetrics.widthPixels;

screenHeight = displayMetrics.heightPixels;

mDensityDpi = displayMetrics.densityDpi /320;

//关闭硬件加速

        setLayerType(LAYER_TYPE_SOFTWARE,null);

//初始化 半径

        raduis = screenWidth /3;

//圆心

        pointX = pointY = screenWidth /2;

bigArcRectF =new RectF(pointX - raduis, pointY - raduis, pointX + raduis, pointY + raduis);

bigArcPaint =new Paint();//最外层的半圆画笔

        bigArcPaint.setStrokeWidth(16 * mDensityDpi);

bigArcPaint.setStrokeCap(Paint.Cap.ROUND);//设置画笔笔触为圆形

        bigArcPaint.setAntiAlias(true);

bigArcPaint.setStyle(Paint.Style.STROKE);

bigArcPaint.setColor(Color.parseColor("#ffffff"));

bigArcPaint.setAlpha(100);

keduRectF =new RectF(pointX - raduis, pointY - raduis, pointX + raduis, pointY + raduis);

keduPaint1 =new Paint();

keduPaint1.setStyle(Paint.Style.STROKE);

keduPaint1.setAntiAlias(true);

// p.setShadowLayer(3F, 2F, 2F, Color.parseColor("#FFF03B"));

        keduPaint1.setColor(Color.parseColor("#FFF03B"));

keduPaint1.setStrokeWidth(16 * mDensityDpi);

keduPaint1.setStrokeCap(Paint.Cap.ROUND);//设置画笔笔触为圆形

        keduPaint2 =new Paint();

keduPaint2.setStyle(Paint.Style.STROKE);

keduPaint2.setAntiAlias(true);

keduPaint2.setColor(Color.parseColor("#FFF03B"));

keduPaint2.setStrokeWidth(1 * mDensityDpi);

keduPaint2.setStrokeCap(Paint.Cap.ROUND);//设置画笔笔触为圆形

        keduPaint2.setStyle(Paint.Style.FILL_AND_STROKE);

outCirclePaint =new Paint();

outCirclePaint.setStyle(Paint.Style.FILL_AND_STROKE);

outCirclePaint.setAntiAlias(true);

outCirclePaint.setColor(Color.parseColor("#ffffff"));

outCirclePaint.setStrokeWidth(1 * mDensityDpi);

Typeface typeface = Typeface.createFromAsset(mContext.getAssets(),"fonts/Haettenschweiler.ttf");

numberPaint =new Paint();

numberPaint.setStyle(Paint.Style.FILL_AND_STROKE);

numberPaint.setAntiAlias(true);

numberPaint.setColor(Color.parseColor("#ffffff"));

numberPaint.setTypeface(typeface);

numberPaint.setStrokeWidth(1 * mDensityDpi);

numberPaint.setTextAlign(Paint.Align.CENTER);

int strokeWidth = raduis /4;//要绘制圆弧的宽度

        xuxianRectF =new RectF(pointX - raduis +10 *2 * mDensityDpi + strokeWidth /2, pointY - raduis +10 *2 * mDensityDpi + strokeWidth /2,

pointX + raduis -10 *2 * mDensityDpi - strokeWidth /2, pointY + raduis -10 *2 * mDensityDpi - strokeWidth /2);

xuxianPaint1 =new Paint();

xuxianPaint1.setStyle(Paint.Style.STROKE);

xuxianPaint1.setAntiAlias(true);

xuxianPaint1.setColor(Color.parseColor("#ffffff"));

xuxianPaint1.setAlpha(30);

xuxianPaint1.setStrokeWidth(raduis /4);

xuxianPaint2 =new Paint();

xuxianPaint2.setAntiAlias(true);

xuxianPaint2.setColor(Color.parseColor("#ffffff"));

xuxianPaint2.setStyle(Paint.Style.STROKE);

xuxianPaint2.setAlpha(100);

xuxianPaint2.setStrokeWidth(5 * mDensityDpi);

keduArcRectF =new RectF(pointX - raduis /2 + raduis /4 /2, pointY - raduis /2 + raduis /4 /2,

pointX + raduis /2 - raduis /4 /2, pointY + raduis /2 - raduis /4 /2);

keduArcPaint1 =new Paint();

keduArcPaint1.setStyle(Paint.Style.STROKE);

keduArcPaint1.setAntiAlias(true);

keduArcPaint1.setColor(Color.parseColor("#F3612B"));

keduArcPaint1.setStrokeWidth(raduis /4);

keduArcPaint2 =new Paint();

keduArcPaint2.setStyle(Paint.Style.STROKE);

keduArcPaint2.setAntiAlias(true);

keduArcPaint2.setColor(Color.parseColor("#c21212"));

keduArcPaint2.setAlpha(50);

keduArcPaint2.setStrokeWidth(3 * mDensityDpi);

smallArcRectF =new RectF(pointX - raduis /4 + mDensityDpi /2, pointY - raduis /4 + mDensityDpi /2,

pointX + raduis /4 - mDensityDpi /2, pointY + raduis /4 - mDensityDpi /2);

smallArcPaint =new Paint();

smallArcPaint.setStyle(Paint.Style.FILL);

smallArcPaint.setAntiAlias(true);

smallArcPaint.setColor(Color.parseColor("#FC8248"));

smallArcPaint.setStrokeWidth(mDensityDpi);

zhizhenPaint =new Paint();

zhizhenPaint.setStyle(Paint.Style.FILL);

zhizhenPaint.setAntiAlias(true);

zhizhenPaint.setColor(Color.parseColor("#FFF0E9"));

zhizhenPaint.setStrokeWidth(mDensityDpi);

}

/**

* 用来计算cosθ和2gr

*/

    private void calCosOAnd2gr() {

//这里为了避免cosα-cosθ=0的情况,所以+0.1

        cosO = (float) Math.cos((angle +0.1f) * Math.PI /180);

//2倍的重力加速度乘以半径

        gr2 = (float) (9.8 * raduis *2);

}

@Override

protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec) {

int widthSize = MeasureSpec.getSize(widthMeasureSpec);

int widthMode = MeasureSpec.getMode(widthMeasureSpec);

int heightSize = MeasureSpec.getSize(heightMeasureSpec);

int heightMode = MeasureSpec.getMode(heightMeasureSpec);

if (widthMode == MeasureSpec.EXACTLY) {

mWidth = widthSize;

}else {

// mWidth = DensityUtils.dp2px(mContext, 200);

            mWidth = screenWidth;

}

if (heightMode == MeasureSpec.EXACTLY) {

mHeight = heightSize;

}else {

// mHeight = DensityUtils.dp2px(mContext, 200);

            mHeight = screenHeight /3;

}

setMeasuredDimension(mWidth, mHeight);

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

if (flag ==1) {//表示指针已经第一次完成数据加载,到达指定位置

            jiaodu2 = (animprogress + degree[0]) * mSweepAngle /100 -1f;

}else {//开始进行小幅度左右摆动

            jiaodu = (animprogress) * mSweepAngle /100;

}

handler.postDelayed(this,1);

drawBigArc(canvas);//最外层的半圆

        drawkedu(canvas);//最外层的半圆的刻度

        drawOutCircle(canvas);//最外层半圆的白点刻度

        drawNumber(canvas);//最外层刻度数字

        drawxuxian(canvas);//透明圆弧和里面的虚线

        drawkeduArc(canvas);//画带刻度的圆弧和刻度

        drawsmallArc(canvas);//画最小的半圆

        drawzhizhen(canvas);//画指针

        if (degree[1] ==1) {

//从左往右,degree增大

            if (degree[0] < angle) {

//计算需要转动的角度

                float changeAngle = rotateAngle();

//改变当前角度的值

                degree[0] = degree[0] + changeAngle;

invalidate();

}

//当达到最右侧时,方向翻转

            if (degree[0] >= angle) {

degree[1] = -1;

}

}else if (degree[1] == -1) {

//从右往左,degree减小

            if (degree[0] > -angle) {

//计算需要转动的角度

                float changeAngle = rotateAngle();

//改变当前角度的值

                degree[0] = degree[0] - changeAngle;

invalidate();

}

//当达到最左侧时,方向翻转

            if (degree[0] <= -angle) {

degree[1] =1;

}

}

}

/**

* 计算当前需要转动的角度

*

    * @return

    */

    private float rotateAngle() {

//计算当前的速率

        float v = (float) Math.sqrt(gr2 * (Math.cos(Math.abs(degree[0]) * Math.PI /180) - cosO));

//计算需要改变的弧度

        float changedAngle = t * v / raduis;

return changedAngle;

}

private void drawzhizhen(Canvas canvas) {

float angle;

if (flag ==1) {//当flag==1时,表示指针需要摆动了,所以angle取jiaodu2,带摆动的值

            angle = jiaodu2;

}else {

angle = jiaodu;

}

float r = raduis /12;

float strokeWidth = mDensityDpi;//要绘制圆弧的宽度

        canvas.drawCircle(pointX, pointY, r, zhizhenPaint);

float sweepAngle =15f;

float r1 =20;

float[] point_r1 = getCoordinatePoint(r1 + r, mStartAngle + angle - sweepAngle);

float[] point_r2 = getCoordinatePoint(raduis -30 * mDensityDpi - strokeWidth /5, mStartAngle + angle);

float[] point_r3 = getCoordinatePoint(r1 + r, mStartAngle + angle + sweepAngle);

// 画指针

        Path mPath =new Path();

mPath.moveTo(pointX, pointY);

mPath.lineTo(point_r1[0], point_r1[1]);

mPath.lineTo(point_r2[0], point_r2[1]);

mPath.lineTo(point_r3[0], point_r3[1]);

mPath.lineTo(pointX, pointY);

mPath.close();

canvas.drawPath(mPath, zhizhenPaint);

}

private void drawsmallArc(Canvas canvas) {

canvas.drawArc(smallArcRectF, mStartAngle, mSweepAngle,false, smallArcPaint);

}

private void drawkeduArc(Canvas canvas) {

int strokeWidth = raduis /4;//要绘制圆弧的宽度

        canvas.drawArc(keduArcRectF, mStartAngle, mSweepAngle,false, keduArcPaint1);

//画刻度

        for (int i =0; i <=35; i++) {

float[] point = getCoordinatePoint(raduis /2, mStartAngle + mSweepAngle /35 * i);

float[] point1 = getCoordinatePoint(raduis /2 - strokeWidth /3, mStartAngle + mSweepAngle /35 * i);

float[] point2 = getCoordinatePoint(raduis /2 - strokeWidth /4, mStartAngle + mSweepAngle /35 * i);

if (i %5 ==0) {//画长刻度

                canvas.drawLine(point[0], point[1], point1[0], point1[1], keduArcPaint2);

}else {//画短刻度

                canvas.drawLine(point[0], point[1], point2[0], point2[1], keduArcPaint2);

}

}

}

private void drawxuxian(Canvas canvas) {

int strokeWidth = raduis /4;//要绘制圆弧的宽度

        canvas.drawArc(xuxianRectF, mStartAngle -1, mSweepAngle +2,false, xuxianPaint1);

for (int i =0; i <= mSection * mPortion; i++) {//画小虚线

            float[] point = getCoordinatePoint(raduis -10 * mDensityDpi - strokeWidth /5, mStartAngle + mSweepAngle / (mSection * mPortion) * i);

float[] point1 = getCoordinatePoint(raduis -10 * mDensityDpi - strokeWidth /5 -4 * mDensityDpi, mStartAngle + mSweepAngle / (mSection * mPortion) * i);

float[] point2 = getCoordinatePoint(raduis -10 * mDensityDpi -2 * strokeWidth /5, mStartAngle + mSweepAngle / (mSection * mPortion) * i);

float[] point3 = getCoordinatePoint(raduis -10 * mDensityDpi -2 * strokeWidth /5 -4 * mDensityDpi, mStartAngle + mSweepAngle / (mSection * mPortion) * i);

float[] point4 = getCoordinatePoint(raduis -10 * mDensityDpi -3 * strokeWidth /5, mStartAngle + mSweepAngle / (mSection * mPortion) * i);

float[] point5 = getCoordinatePoint(raduis -10 * mDensityDpi -3 * strokeWidth /5 -4 * mDensityDpi, mStartAngle + mSweepAngle / (mSection * mPortion) * i);

canvas.drawLine(point[0], point[1], point1[0], point1[1], xuxianPaint2);

if ((i >1 * mPortion && i < mPortion *2) || (i >5 * mPortion && i < mPortion *6)) {

}else {

canvas.drawLine(point2[0], point2[1], point3[0], point3[1], xuxianPaint2);

}

if ((i >8 && i <16) || (i >4 * mPortion && i < mPortion *6)) {

}else {

canvas.drawLine(point4[0], point4[1], point5[0], point5[1], xuxianPaint2);

}

}

}

private void drawNumber(Canvas canvas) {

Rect bounds =new Rect();

for (int i =0; i < mSection +1; i++) {

String testString =50 * i +"";

float[] point = getCoordinatePoint(raduis +70 * mDensityDpi, mStartAngle + i * (float) (mSweepAngle / mSection));

if (jiaodu >= i * (float) (mSweepAngle / mSection)) {

numberPaint.setAlpha(255);

numberPaint.setTextSize(DensityUtils.sp2px(mContext,16));

}else {

numberPaint.setAlpha(100);

numberPaint.setTextSize(DensityUtils.sp2px(mContext,10));

}

numberPaint.getTextBounds(testString,0, testString.length(), bounds);

if (i <4) {

canvas.drawText(testString, point[0] - bounds.width() /2, point[1] + bounds.height() /2, numberPaint);

}else {

canvas.drawText(testString, point[0] + bounds.width() /2, point[1] + bounds.height() /2, numberPaint);

}

}

}

private void drawOutCircle(Canvas canvas) {

//        for (int i = 0; i < mSection + 1; i++) {

//            //指针扫过角度的圆点

//            float[] point = getCoordinatePoint(raduis + 25 * mDensityDpi, mStartAngle + i * (float) (mSweepAngle / mSection));

//            canvas.drawCircle(point[0], point[1], 10 * mDensityDpi, p);

//        }

        for (int i =0; i <=5 * mSection; i++) {

float[] point = getCoordinatePoint(raduis +35 * mDensityDpi, mStartAngle + i * (float) (mSweepAngle / (mSection *5)));

if (jiaodu >= i * (float) (mSweepAngle / (mSection *5))) {

outCirclePaint.setAlpha(255);

}else {

outCirclePaint.setAlpha(100);

}

if (i %5 ==0) {

//指针扫过角度的大圆点

                canvas.drawCircle(point[0], point[1],6 * mDensityDpi, outCirclePaint);

}else {

//指针扫过角度的小圆点

                canvas.drawCircle(point[0], point[1],3 * mDensityDpi, outCirclePaint);

}

}

}

private void drawkedu(Canvas canvas) {

canvas.drawArc(keduRectF, mStartAngle, jiaodu,false, keduPaint1);

//指针扫过角度的圆点

        float[] point = getCoordinatePoint(raduis, mStartAngle + jiaodu);

canvas.drawCircle(point[0], point[1],15 * mDensityDpi, keduPaint2);

}

private void drawBigArc(Canvas canvas) {

canvas.drawArc(bigArcRectF, mStartAngle, mSweepAngle,false, bigArcPaint);

}

//将角度和半径转为点

    private float[] getCoordinatePoint(float radius,float angle) {

float[] point =new float[2];

double arcAngle = Math.toRadians(angle);//将角度转换为弧度

        if (angle <90) {

point[0] = (float) (pointX + Math.cos(arcAngle) * radius);

point[1] = (float) (pointY + Math.sin(arcAngle) * radius);

}else if (angle ==90) {

point[0] = pointX;

point[1] = pointY + radius;

}else if (angle >90 && angle <180) {

arcAngle = Math.PI * (180 - angle) /180.0;

point[0] = (float) (pointX - Math.cos(arcAngle) * radius);

point[1] = (float) (pointY + Math.sin(arcAngle) * radius);

}else if (angle ==180) {

point[0] = pointX - radius;

point[1] = pointY;

}else if (angle >180 && angle <270) {

arcAngle = Math.PI * (angle -180) /180.0;

point[0] = (float) (pointX - Math.cos(arcAngle) * radius);

point[1] = (float) (pointY - Math.sin(arcAngle) * radius);

}else if (angle ==270) {

point[0] = pointX;

point[1] = pointY - radius;

}else {

arcAngle = Math.PI * (360 - angle) /180.0;

point[0] = (float) (pointX + Math.cos(arcAngle) * radius);

point[1] = (float) (pointY - Math.sin(arcAngle) * radius);

}

return point;

}

@Override

public void run() {

if (animprogress >= zhizhenValue) {

flag =1;

invalidate();

return;

}else {

animprogress +=1;

invalidate();

}

}

}