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();
}
}
}
上一篇: InitOpts:初始化配置项
下一篇: PyEcharts 基本图表之日历图