找了两个仪表盘的文章,我觉得比较好看,分别如下所示。
1
代码
由三部分组成
- 创建
CircleProgressView.java
- 创建
values/attr.xml
- 调用方法
CircleProgressView.java
/**
* Created by kang on 2020/12/3.
*/
public class CircleProgressView extends View {
/**
* 直径
*/
private int mDiameter;
/**
* 绘制时控制绘制的范围
*/
private Paint mPaint;
private float progressValue = 0;
RectF rect;
public CircleProgressView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CircleProgressView(Context context) {
this(context, null);
}
/**
* 获得自定义的样式属性
*
* @param context
* @param attrs
* @param defStyle
*/
public CircleProgressView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CircleProgressView, defStyle, 0);
int n = a.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = a.getIndex(i);
switch (attr) {
case R.styleable.CircleProgressView_diameter:
// 默认设置为40dp
mDiameter = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 40, getResources().getDisplayMetrics()));
break;
}
}
a.recycle();
mPaint = new Paint();
rect = new RectF();
progressValue = 0;
}
public void setProgressValue(float progressValue) {
this.progressValue = progressValue;
postInvalidate();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = 0;
int height = 0;
//设置直径的最小值
if (mDiameter <= 40) {
mDiameter = 40;
}
height = mDiameter;
width = mDiameter;
setMeasuredDimension(width, height);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int mWidth = getMeasuredWidth();//得到测量宽度
int mHeight = getMeasuredHeight();//得到测量高度
/************************************绘制圆弧***********************************/
float section = progressValue / 100;//progressValue是传进来的值,由定时器不停地发送过来
mPaint.setAntiAlias(true);//消除锯齿
mPaint.setStrokeWidth((float) 11);//画笔宽度
mPaint.setStyle(Paint.Style.STROKE);//描边
mPaint.setStrokeCap(Paint.Cap.ROUND);//ROUND,表示是圆角的笔触
rect.set(20, 20, mWidth - 20, mHeight - 20);//矩形区域,用来限制绘制的圆弧范围
mPaint.setColor(Color.BLACK);
/*
颜色线性渐变
LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1,Shader.TileMode tile)
float x0: 渐变起始点x坐标
float y0:渐变起始点y坐标
float x1:渐变结束点x坐标
float y1:渐变结束点y坐标
int color0: 起始渐变色
int color1: 结束渐变色
Shader.TileMode tile: CLAMP边缘拉伸
*/
LinearGradient shader = new LinearGradient(3, 3, mWidth - 3, mHeight - 3, Color.parseColor("#4299ff"), Color.parseColor("#3cedff"), Shader.TileMode.CLAMP);
mPaint.setShader(shader);
//画弧,第一个参数是RectF,第二个参数是角度的开始,第三个参数是旋转多少度,第四个参数是true的时候画扇形,是false的时候画弧线
canvas.drawArc(rect, 90, section * 360, false, mPaint);
/************************************绘制小圆球***********************************/
mPaint.setStyle(Paint.Style.FILL);//填充
mPaint.setColor(Color.BLUE);
float radius = rect.height() / 2;//半径
//获取圆弧的圆心坐标
PointF mCenterPoint = new PointF(rect.centerX(), rect.centerY());
PointF mRunCirclePoint = new PointF();//小圆球的圆心坐标
float a = section * 360 + 90;//小球圆心和圆弧圆心连线与x轴的夹角(顺时针旋转)
if (a >= 90 && a < 180) {
mRunCirclePoint.x = mCenterPoint.x - radius * (float) Math.sin((a - 90) * (2 * Math.PI / 360));
mRunCirclePoint.y = mCenterPoint.y + radius * (float) Math.cos((a - 90) * (2 * Math.PI / 360));
Log.i("CircleProgressView", "第三象限: x=" + mRunCirclePoint.x + ",y=" + mRunCirclePoint.y);
}
if (a >= 180 && a < 270) {
mRunCirclePoint.x = mCenterPoint.x - radius * (float) Math.cos((a - 180) * (2 * Math.PI / 360));
mRunCirclePoint.y = mCenterPoint.y - radius * (float) Math.sin((a - 180) * (2 * Math.PI / 360));
Log.i("CircleProgressView", "第四象限: x=" + mRunCirclePoint.x + ",y=" + mRunCirclePoint.y);
}
if (a >= 270 && a < 360) {
mRunCirclePoint.x = mCenterPoint.x + radius * (float) Math.sin((a - 270) * (2 * Math.PI / 360));
mRunCirclePoint.y = mCenterPoint.y - radius * (float) Math.cos((a - 270) * (2 * Math.PI / 360));
Log.i("CircleProgressView", "第一象限: x=" + mRunCirclePoint.x + ",y=" + mRunCirclePoint.y);
}
if (a >= 360 && a <= 450) {
mRunCirclePoint.x = mCenterPoint.x + radius * (float) Math.cos((a - 360) * (2 * Math.PI / 360));
mRunCirclePoint.y = mCenterPoint.y + radius * (float) Math.sin((a - 360) * (2 * Math.PI / 360));
Log.i("CircleProgressView", "第二象限: x=" + mRunCirclePoint.x + ",y=" + mRunCirclePoint.y);
}
//绘制圆球,参数一是中心点的x轴,参数二是中心点的y轴,参数三是半径,参数四是paint对象
canvas.drawCircle(mRunCirclePoint.x, mRunCirclePoint.y, 10, mPaint);
}
}
values/attr.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="diameter" format="dimension" />
<declare-styleable name="CircleProgressView">
<attr name="diameter" />
</declare-styleable>
</resources>
调用
布局xml调用如下
<com.example.k.practice.CircleProgressView
android:id="@+id/CircleProgressView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
custom:diameter="180px" />
如果直接调用setProgressValue()
方法可以显示,但是不会动。
new一个TimeTask
,负责传递消息。
发送消息给Handler
处理,调用setProgressValue()
方法。
然后使用Time
设定一个时间。这个时间有点慢,也就是这个时间内到达,这个时间有点慢可以自己改。
好像还可以在里面显示字体,但是不知道怎么显示,外面也可以显示一圈刻度。
/**
* Created by kang on 2020/11/18.
*/
public class MainHomeFragmentClass extends Fragment {
CircleProgressView ProgressView;
TimerTask task4;
float progressValue4=0;
private Handler handler;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view=inflater.inflate(R.layout.main_home_fragment,container,false);
ProgressView=view.findViewById(R.id.CircleProgressView);
task4 = new TimerTask() {
@Override
public void run() {
Message message = Message.obtain();
message.what = 4;
handler.sendMessage(message);
}
};
handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if (msg.what == 4) {
//空气湿度
ProgressView.setProgressValue(progressValue4);//设置传入的值
progressValue4 += 0.1;
if (progressValue4 > 80) {// h 即为目标值(空气湿度)
task4.cancel();
}
}
return false;
}
});
Timer timer = new Timer();
timer.schedule(task4, 1000, 10);
return view;
}
}
2
这个挺好的。
代码
由两部分组成
- 创建
CircleProgressView.java
- 调用方法
DashboardView.java
在这个文件里几个地方,就是我注释的地方,好像可以修改字体大小。
/**
* Created by kang on 2020/12/3.
*/
public class DashboardView extends View {
private int padding = 20;
private int mWidth;
private int mHeight;
private float progress;
private Paint mScalePaint;
private Paint mCiclePaint;
private Paint mTextPaint;
private Paint mPointPaint;
private Paint mCenterCiclePaint;
private Paint mProgressTextPaint;
public DashboardView(Context context) {
this(context,null);
}
public DashboardView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public DashboardView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mScalePaint = new Paint();
mScalePaint.setDither(true);
mScalePaint.setAntiAlias(true);
mScalePaint.setColor(Color.BLACK);
mScalePaint.setStyle(Paint.Style.STROKE);
mScalePaint.setStrokeWidth(2);
mCiclePaint = new Paint();
mCiclePaint.setDither(true);
mCiclePaint.setAntiAlias(true);
mCiclePaint.setStyle(Paint.Style.STROKE);
mCiclePaint.setStrokeWidth(20);
mTextPaint = new Paint();
mTextPaint.setStyle(Paint.Style.STROKE);
//mTextPaint.setTextSize(DisPlayUtils.dp2px(12));
mTextPaint.setTextSize(12);
mTextPaint.setAntiAlias(true);
mTextPaint.setColor(Color.BLACK);
mPointPaint = new Paint();
mPointPaint.setAntiAlias(true);
mPointPaint.setDither(true);
mPointPaint.setColor(Color.RED);
mCenterCiclePaint = new Paint();
mCenterCiclePaint.setColor(Color.WHITE);
mCenterCiclePaint.setAntiAlias(true);
mCenterCiclePaint.setDither(true);
mProgressTextPaint = new Paint();
mProgressTextPaint.setDither(true);
mProgressTextPaint.setAntiAlias(true);
mProgressTextPaint.setStyle(Paint.Style.STROKE);
//mProgressTextPaint.setTextSize(DisPlayUtils.dp2px(20));
mProgressTextPaint.setTextSize(20);
SweepGradient gradient = new SweepGradient(0,0,new int[]{Color.parseColor("#3bc1ff"), Color.parseColor("#3fc6fa"),
Color.parseColor("#5fc6d2"),Color.parseColor("#90c691"),Color.parseColor("#ccc643"),
Color.parseColor("#fdc602"),Color.parseColor("#ff9300"),Color.parseColor("#ff5100"),
Color.parseColor("#fa2300"),Color.parseColor("#ee0d07"),Color.parseColor("#d3162b"),},null);
Matrix gradientMatrix = new Matrix();
gradientMatrix.preRotate(135);
gradient.setLocalMatrix(gradientMatrix);
mCiclePaint.setShader(gradient);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = getWidth();
mHeight = getHeight();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(mWidth / 2, mHeight / 2);//移动坐标原点到中心
RectF rectF = new RectF(-mWidth/2 + padding,-mHeight/2+padding,mWidth/2-padding,mHeight/2-padding);
canvas.drawArc(rectF,135,270,false,mCiclePaint);
drawNum(canvas);//画仪表盘刻度
drawerPointer(canvas,progress);//画指针
drawCenterCircle(canvas);//画指针圆心
drawProgressText(canvas); //画仪表盘进度值
}
public void setProgress(float progress){
if (progress<0)progress=0;
if (progress>100)progress=100;
this.progress = progress;
invalidate();
}
private void drawNum(Canvas canvas){
canvas.save();
canvas.rotate(-135,0,0);
int indexY = -mHeight/2+10;
canvas.drawLine(0,indexY+20,0,indexY+40,mScalePaint);
float rAngle = 270/(100*1.0f);
for (int i = 0; i <=100 ; i++) {
canvas.save(); //记录画布状态
canvas.rotate(rAngle * i, 0, 0);
if (i==0 || i%5==0){
canvas.drawLine(0,indexY+20,0,indexY+40,mScalePaint);
if (i%10==0){
//canvas.drawText(i+"",-DisPlayUtils.dp2px(6),indexY+70,mTextPaint);
canvas.drawText(i+"",-6,indexY+70,mTextPaint);
}
}else {
canvas.drawLine(0,indexY+20,0,indexY+30,mScalePaint);
}
canvas.restore();
}
canvas.restore();
}
private void drawerPointer(Canvas canvas,float progress){
canvas.save();
canvas.rotate( -135+270/(100*1.0f)*progress);
Path pointPath = new Path();
RectF pointF = new RectF(-10,-10,10,10);
pointPath.moveTo(0,-mWidth/4);
pointPath.lineTo(-10,0);
pointPath.addArc(pointF,0,180);
pointPath.lineTo(0,-mWidth/4);
canvas.drawPath(pointPath,mPointPaint);
canvas.restore();
}
private void drawCenterCircle(Canvas canvas){
canvas.drawCircle(0,0,5,mCenterCiclePaint);
}
private void drawProgressText(Canvas canvas){
String text = progress+"%";
if ( progress>=0 && progress<=60){
mProgressTextPaint.setColor(Color.parseColor("#3bc1ff"));
}else if (progress>60&&progress<=80){
mProgressTextPaint.setColor(Color.parseColor("#ff5100"));
}else if (progress>80){
mProgressTextPaint.setColor(Color.parseColor("#ee0d07"));
}
//canvas.drawText(progress+"%",-getTextLength(mProgressTextPaint,text)/2,mHeight/2-DisPlayUtils.dp2px(10),mProgressTextPaint);
canvas.drawText(progress+"%",-getTextLength(mProgressTextPaint,text)/2,mHeight/2-10,mProgressTextPaint);
}
private float getTextLength(Paint paint,String text){
return paint.measureText(text);
}
}
调用
布局xml文件内容如下,宽高最好差不多。
<com.example.k.practice.DashboardView
android:id="@+id/DashboardView"
android:layout_width="400dp"
android:layout_height="400dp"
android:layout_gravity="center"/>
如果直接调用setProgress()
方法可以显示,但是不会动。
new一个TimeTask
,负责传递消息。
发送消息给Handler
处理,调用setProgress()
方法。
然后使用Time
设定一个时间。这个时间有点慢,也就是这个时间内到达,这个时间有点慢可以自己改。
/**
* Created by kang on 2020/11/18.
*/
public class MainHomeFragmentClass extends Fragment {
DashboardView Dashboard;
TimerTask task4;
float progressValue4=0;
private Handler handler;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view=inflater.inflate(R.layout.main_home_fragment,container,false);
Toast.makeText(getContext(),"这是主页碎片",Toast.LENGTH_SHORT).show();
Dashboard=view.findViewById(R.id.DashboardView);
//Dashboard.setProgress(5);
task4=new TimerTask() {
@Override
public void run() {
Message message = Message.obtain();
message.what = 4;
handler.sendMessage(message);
}
};
handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if (msg.what == 4) {
//空气湿度
Dashboard.setProgress(progressValue4);//设置传入的值
progressValue4 += 0.1;
if (progressValue4 > 30) {// h 即为目标值(空气湿度)
task4.cancel();
}
}
return false;
}
});
Timer timer = new Timer();
timer.schedule(task4, 1000, 10);
return view;
}
}