实现效果:

项目需要一个实时显示车速的仪表盘,没找到合适的控件,就自己写了一个.记录下,以后方便查看.

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RadialGradient;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.SweepGradient;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;import androidx.annotation.Nullable;/*** Date :2021/12/24* Time :21:24* Author:moyihen* Description: 仪表盘* * 注意:因为是在固定开发板用,没怎么考虑适配情况.* 1.表盘大小是根据控件布局文件的width决定* 2.控件宽度太小可能刻度文字显示不全,可以看情况调整下文字大小.*/
public class DashboardView extends View {private static final String TAG = "DashboardView";private Paint arcPaint;//圆环的角度private int SWEEPANGLE = 280;//刻度画笔private Paint pointerPaint;private Context mContext;//圆环半径private int mRadius;//圆环的宽度int arcW = 10;//发光的宽度int gleamyArcW = arcW * 3;//刻度宽度int minScalew = 5;int maxScalew = 5;//刻度的长度int maxScaleLength = 50;int minScaleLength = 30;private Paint gleamyArcPaint;//发光圆环的半径private int mRadiusG;//阴影宽度private int shade_w = 40;private Path pointerPath;private float currentDegree = 0;//指针当前的角度.private int startAngele = 90 + (360 - SWEEPANGLE) / 2;//仪表盘显示的数字private String speed = "0";private Paint mTextPaint;private Paint mPaint;private ValueAnimator mAnim;public DashboardView(Context context) {super(context);init(context);}public DashboardView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);init(context);}private void init(Context context) {mContext = context;// 关闭硬件加速setLayerType(LAYER_TYPE_SOFTWARE, null);//外层动态圆环画笔mPaint = new Paint();mPaint.setStyle(Paint.Style.STROKE);//画线模式mPaint.setAntiAlias(true);//圆环画笔arcPaint = new Paint();arcPaint.setStyle(Paint.Style.STROKE);//画线模式arcPaint.setStrokeWidth(arcW);//线宽度arcPaint.setColor(Color.parseColor("#07A6EC"));arcPaint.setAntiAlias(true);//刻度pointerPaint = new Paint();pointerPaint.setAntiAlias(true);pointerPaint.setColor(Color.parseColor("#26396F"));pointerPaint.setTextSize(40);pointerPaint.setTextAlign(Paint.Align.RIGHT);pointerPath = new Path();//发光圆环gleamyArcPaint = new Paint();gleamyArcPaint.setAntiAlias(true);gleamyArcPaint.setStyle(Paint.Style.STROKE);gleamyArcPaint.setStrokeWidth(gleamyArcW);//文字mTextPaint = new Paint();mTextPaint.setAntiAlias(true);mTextPaint.setColor(Color.WHITE);mTextPaint.setTextAlign(Paint.Align.CENTER);mTextPaint.setTextSize(60);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);mRadius = (int) (getMeasuredWidth() / 2 * 0.8);mRadiusG = mRadius - gleamyArcW / 2;shade_w = (int) (mRadius * 0.4);//Log.i(TAG, "onDraw: mRadius"+mRadius+"mRadiusG:"+mRadiusG+"shade_w:"+shade_w);//表盘背景颜色canvas.drawColor(Color.parseColor("#040613"));//canvas.drawColor(Color.WHITE);//Log.i(TAG, "onDraw1: w:"+getMeasuredWidth()+"h:"+getMeasuredHeight());//Log.i(TAG, "onDraw2: w:"+getWidth()+"h:"+getHeight());//最外层白色动态圆环drawDynamicArcs(canvas);//圆环drawArcs(canvas);//发光圆环drawGleamyArc(canvas);//刻度drawDegree(canvas);//指针阴影drawShade(canvas);//黑色圆形背景drawCircleBlack(canvas);//指针drawPointer(canvas);//中心圆环drawCenterArcs(canvas);//中心显示文字drawCenterText(canvas);}private void drawDynamicArcs(Canvas canvas) {//半径int dyRaduis = (int) (getMeasuredWidth() / 2 * 0.88);int w = 10;int[] colorSweep = new int[]{Color.parseColor("#00FFFFFF"), Color.parseColor("#FFFFFFFF")};float[] position = new float[]{0f, 0.5f};SweepGradient mShader = new SweepGradient(getMeasuredWidth() / 2, getMeasuredHeight() / 2, colorSweep, position);//旋转渐变Matrix matrix = new Matrix();matrix.setRotate(startAngele, canvas.getWidth() / 2, canvas.getHeight() / 2);mShader.setLocalMatrix(matrix);mPaint.setShader(mShader);//arcPaint.setStyle(Paint.Style.STROKE);mPaint.setStrokeWidth(w);RectF rectF = new RectF();rectF.left = (float) (getMeasuredWidth() / 2 - (dyRaduis - w / 2));rectF.top = (float) (getMeasuredHeight() / 2 - (dyRaduis - w / 2));rectF.right = (float) (getMeasuredWidth() / 2 + (dyRaduis - w / 2));rectF.bottom = (float) (getMeasuredHeight() / 2 + (dyRaduis - w / 2));canvas.drawArc(rectF, 90 + (360 - SWEEPANGLE) / 2, currentDegree, false, mPaint);}private void drawCenterText(Canvas canvas) {mTextPaint.reset();mTextPaint.setColor(Color.WHITE);mTextPaint.setTextAlign(Paint.Align.CENTER);mTextPaint.setTextSize(60);mTextPaint.setAntiAlias(true);double mm = mRadius * 0.4;RectF rect = new RectF();rect.left = (float) (getMeasuredWidth() / 2 - mm);rect.top = (float) (getMeasuredHeight() / 2 - mm);rect.right = (float) (getMeasuredWidth() / 2 + mm);rect.bottom = (float) (getMeasuredHeight() / 2 + mm);Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();float distance = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;float baseline = rect.centerY() + distance;//速度canvas.drawText(speed, rect.centerX(), baseline, mTextPaint);//canvas.drawText(speed,getMeasuredWidth()/2,getMeasuredHeight()/2,pointerPaint);mTextPaint.setTextSize(40);//绘制底部文字(向下20px)float text_h = Math.abs(fontMetrics.top - fontMetrics.bottom) - 20;//Log.i(TAG, "drawCenterText: "+fontMetrics.bottom+"=="+fontMetrics.top+"=="+text_w);String info = speed + "Wh/km";canvas.drawText(info, getMeasuredWidth() / 2, (float) (getMeasuredHeight() / 2) + (mRadius - text_h), mTextPaint);//速度文字下划线float text_w = mTextPaint.measureText(info);// Log.i(TAG, "drawCenterText:aaaa"+text_w);//pointerPaint.setShadowLayer(10, 0, 0, Color.parseColor("#f8b62e"));int[] color = {Color.parseColor("#80041B25"), Color.parseColor("#0496C6"), Color.parseColor("#80041B25")};float[] position = {0f, 0.5f, 1f};mTextPaint.setStrokeWidth(3);mTextPaint.setShader(new LinearGradient((float) (getMeasuredWidth() / 2) - text_w / 2, (float) (getMeasuredHeight() / 2) + (mRadius - text_h + 10),(float) (getMeasuredWidth() / 2) + text_w / 2, (float) (getMeasuredHeight() / 2) + (mRadius - text_h + 10), color, position, Shader.TileMode.MIRROR));canvas.drawLine((float) (getMeasuredWidth() / 2) - text_w / 2, (float) (getMeasuredHeight() / 2) + (mRadius - text_h + 10), (float) (getMeasuredWidth() / 2) + text_w / 2, (float) (getMeasuredHeight() / 2) + (mRadius - text_h + 10), mTextPaint);}private void drawCenterArcs(Canvas canvas) {//中心发光圆环pointerPaint.setColor(Color.parseColor("#050D3D"));pointerPaint.setStyle(Paint.Style.FILL);pointerPaint.setShadowLayer(15, 0, 0, Color.parseColor("#006EC6"));canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, (float) (mRadius * 0.4), pointerPaint);//内部深色实心圆pointerPaint.setColor(Color.parseColor("#040613"));canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2,(float) (mRadius * 0.4) - pointerPaint.getStrokeWidth(), pointerPaint);}private void drawPointer(Canvas canvas) {canvas.save();canvas.translate(getMeasuredWidth() / 2, getMeasuredHeight() / 2);canvas.rotate(startAngele + currentDegree);pointerPaint.setColor(Color.WHITE);pointerPath.moveTo(mRadius, 0);pointerPath.lineTo(0, 0 - 5);pointerPath.lineTo(0, 0 + 5);pointerPath.close();canvas.drawPath(pointerPath, pointerPaint);canvas.restore();}private void drawCircleBlack(Canvas canvas) {//Paint pointerPaint = new Paint();//pointerPaint.setAntiAlias(true);pointerPaint.setStyle(Paint.Style.FILL);pointerPaint.setColor(Color.parseColor("#040613"));//pointerPaint.setColor(Color.parseColor("#f8b62e"));canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, (float) (mRadius * 0.6), pointerPaint);}//指针阴影private void drawShade(Canvas canvas) {int[] colorSweep = new int[]{0x66FFE9EC, 0x0328E9EC, 0x1a28E9EC, 0x66FFE9EC};float[] position = new float[]{0f, 0.36f, 0.5f, 0.7f};SweepGradient mShader = new SweepGradient(getMeasuredWidth() / 2, getMeasuredHeight() / 2, colorSweep, position);gleamyArcPaint.setShader(mShader);//arcPaint.setStyle(Paint.Style.STROKE);gleamyArcPaint.setStrokeWidth(shade_w);//arcPaint.clearShadowLayer();RectF rectF = new RectF();rectF.left = (float) (getMeasuredWidth() / 2 - (mRadiusG - shade_w / 2));rectF.top = (float) (getMeasuredHeight() / 2 - (mRadiusG - shade_w / 2));rectF.right = (float) (getMeasuredWidth() / 2 + (mRadiusG - shade_w / 2));rectF.bottom = (float) (getMeasuredHeight() / 2 + (mRadiusG - shade_w / 2));canvas.drawArc(rectF, 90 + (360 - SWEEPANGLE) / 2, currentDegree, false, gleamyArcPaint);}//画发光圆private void drawGleamyArc(Canvas canvas) {gleamyArcPaint.setStrokeWidth(gleamyArcW);int[] a = {Color.parseColor("#000947C3"), Color.parseColor("#ff0947C3")};float[] b = {0.9f, 1f};RadialGradient radialGradient = new RadialGradient(getMeasuredWidth() / 2, getMeasuredHeight() / 2, mRadius - arcW, a, b, Shader.TileMode.CLAMP);//gleamyArcPaint.clearShadowLayer();gleamyArcPaint.setShader(radialGradient);RectF rectF = new RectF(getMeasuredWidth() / 2 - mRadiusG, getMeasuredHeight() / 2 - mRadiusG,getMeasuredWidth() / 2 + mRadiusG, getMeasuredHeight() / 2 + mRadiusG);canvas.drawArc(rectF, 90 + (360 - SWEEPANGLE) / 2, SWEEPANGLE, false, gleamyArcPaint);//canvas.drawArc();}int clockPointNum = 36;private void drawDegree(Canvas canvas) {pointerPaint.setColor(Color.parseColor("#26396F"));pointerPaint.setTextSize(40);pointerPaint.clearShadowLayer();pointerPaint.setTextAlign(Paint.Align.RIGHT);canvas.save();//原点移到空间中心点canvas.translate(getMeasuredWidth() / 2, getMeasuredHeight() / 2);//canvas.rotate(30);canvas.rotate((360 - SWEEPANGLE) / 2 + 90);//(360-240)/2+90;//设置刻度文字颜色大小.mTextPaint.reset();mTextPaint.setColor(Color.parseColor("#26396F"));mTextPaint.setTextSize(40);mTextPaint.clearShadowLayer();mTextPaint.setTextAlign(Paint.Align.RIGHT);mTextPaint.setAntiAlias(true);for (int i = 0; i < clockPointNum; i++) {if (i % 4 == 0) {     //长表针pointerPaint.setStrokeWidth(maxScalew);canvas.drawLine(mRadiusG - arcW, maxScalew / 2, mRadiusG - maxScaleLength, maxScalew / 2, pointerPaint);//Log.i(TAG, "drawDegree: 长刻度I"+i);drawPointerText(canvas, mRadiusG - arcW, maxScalew / 2, i);} else {    //短表针pointerPaint.setStrokeWidth(minScalew);canvas.drawLine(mRadiusG - arcW, maxScalew / 2, mRadiusG - minScaleLength, maxScalew / 2, pointerPaint);}canvas.rotate((float) SWEEPANGLE / (float) clockPointNum);// Log.i(TAG, "onDraw: "+i+"---"+(float) SWEEPANGLE/ (float) clockPointNum);}//最后一根canvas.drawLine(mRadiusG - arcW, -maxScalew / 2, mRadiusG - maxScaleLength, -maxScalew / 2, pointerPaint);drawPointerText(canvas, mRadiusG - arcW, maxScalew / 2, 36);canvas.restore();count = 0;}int count = 0;private void drawPointerText(Canvas canvas, int x, int y, int i) {//动态设置刻度文字颜色。float a = (float) SWEEPANGLE / (float) clockPointNum;if (currentDegree > a * i)mTextPaint.setColor(Color.WHITE);elsemTextPaint.setColor(Color.parseColor("#26396F"));if (i != 0)count += 20;canvas.drawText(String.valueOf(count), x - maxScaleLength, y, mTextPaint);}private void drawArcs(Canvas canvas) {RectF rectF = new RectF(getMeasuredWidth() / 2 - mRadius, getMeasuredHeight() / 2 - mRadius,getMeasuredWidth() / 2 + mRadius, getMeasuredHeight() / 2 + mRadius);canvas.drawArc(rectF, 90 + (360 - SWEEPANGLE) / 2, SWEEPANGLE, false, arcPaint);}/*** 外部更新刻度.** @param speedssss .*/public void udDataSpeed(int speedssss) {float a = SWEEPANGLE / 180f;if (speedssss < 0) throw new IllegalArgumentException("----speed不能小于0----");speed = String.valueOf(speedssss);//Log.i(TAG, "udDataSpeed: \ncurrentDegree:"+currentDegree+"\na:"+a+"\nspeedssss:"+speedssss);startAnimation(currentDegree, (float) speedssss * a);}//指针+阴影偏移动画.private void startAnimation(float start, float end) {if (mAnim!=null){if (mAnim.isRunning()||mAnim.isStarted()){mAnim.cancel();mAnim.removeAllUpdateListeners();}boolean running = mAnim.isRunning();boolean started = mAnim.isStarted();Log.i(TAG, "startAnimation: running:"+running+"--started"+started);}mAnim = ValueAnimator.ofFloat(start, end);//anim.setRepeatCount(ValueAnimator.INFINITE);//设置无限重复//anim.setRepeatMode(ValueAnimator.REVERSE);//设置重复模式mAnim.setDuration(500);mAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator valueAnimator) {float value = (float) mAnim.getAnimatedValue();//Log.i(TAG, "onAnimationUpdate: " + value);currentDegree = value;invalidate();}});mAnim.start();}/*** 退出动画.*/public void closeAnimation() {if (mAnim != null) {mAnim.cancel();mAnim.removeAllUpdateListeners();}}
}
/外部更新
mDashboardView.udDataSpeed(i);

效果:

end:

Android 自定义仪表盘相关推荐

  1. Android 自定义View -- 简约的折线图

    转载请注明出处:http://write.blog.csdn.net/postedit/50434634 接上篇 Android 圆形百分比(进度条) 自定义view 昨天分手了,不开心,来练练自定义 ...

  2. Android自定义view之基础知识

    Android自定义view之基础知识 虽然Android已经自带了很多实用的view和layout,加以调教能实现很美观的界面,但是有一些情况下,需要实现特殊的界面效果,比如我们比较熟悉的各种播放器 ...

  3. Android自定义ViewGroup基本步骤

    1.自定义属性,获取自定义属性,可参考 ​ Android自定义View基本步骤 ​ 2.onMeasure() 方法,for循环测量子View,根据子View的宽高来计算自己的宽 高 3.onDra ...

  4. Android自定义View —— TypedArray

    在上一篇中Android 自定义View Canvas -- Bitmap写到了TypedArray 这个属性 下面也简单的说一下TypedArray的使用 TypedArray 的作用: 用于从该结 ...

  5. Android 自定义View —— Canvas

    上一篇在android 自定义view Paint 里面 说了几种常见的Point 属性 绘制图形的时候下面总有一个canvas ,Canvas 是是画布 上面可以绘制点,线,正方形,圆,等等,需要和 ...

  6. android 自定义loading,Android自定义动画-StarLoadingView

    今天来分享第二个自定义loading的动画,起了个名字叫 蹦跶的星星 ,还是老规矩先介绍,后上图. 实现效果在最后,GIF有点大,手机流量慎重. 介绍 首先声明做这个动画的初衷是为了学习和分享,所以从 ...

  7. android自定义view获取控件,android 自定义控件View在Activity中使用findByViewId得到结果为null...

    转载:http://blog.csdn.net/xiabing082/article/details/48781489 1.  大家常常自定义view,,然后在xml 中添加该view 组件..如果在 ...

  8. android 自定义命名空间,Android自定义ActionBar实例

    本文实例讲述了android自定义actionbar的实现方法.分享给大家供大家参考.具体实现方法如下: android 3.0及以上已经有了actionbar的api,可以通过引入support p ...

  9. Android自定义View:ViewGroup(三)

    自定义ViewGroup本质是什么? 自定义ViewGroup本质上就干一件事--layout. layout 我们知道ViewGroup是一个组合View,它与普通的基本View(只要不是ViewG ...

  10. Android自定义视图四:定制onMeasure强制显示为方形

    这个系列是老外写的,干货!翻译出来一起学习.如有不妥,不吝赐教! Android自定义视图一:扩展现有的视图,添加新的XML属性 Android自定义视图二:如何绘制内容 Android自定义视图三: ...

最新文章

  1. 【 js 片段 】如何组织表单的默认提交?【亲测有效】
  2. 自我回答,问题3:Scroller的属性 scrollPolicyV怎么无效呢,,也就是 scrollPolicyV设置为on,但是并没有显示滚动条...
  3. 003thinkphp 数据库查询及表关联
  4. php获取昨日时间段内,PHP 获取 特定时间范围 类
  5. 帝国cms listinfo.php,帝国CMS动态列表应用之在列表中显示指定的会员组会员发布的信息...
  6. loadrunner-4-4事务摘要分析
  7. Bregman 散度
  8. JS对大陆车牌照的归属地(省份和城市)加载和显示
  9. Office产品 “您的组织策略阻止我们为您完成此操作” 解决办法
  10. 1KB文件夹快捷方式病毒清除方案
  11. 井下三专两闭锁的内容_三专两闭锁
  12. python连接mysql orm_Python通过ORM方式操作MySQL数据库
  13. Python从入门到数据分析第一篇—Python简介- Python介绍与初探
  14. java区间并集_区间并集求解算法实现
  15. Asciinema - 终端日志记录神器,机器学习开发者的福音
  16. java个十百千万位余数_1 Java第三课[流程控制]
  17. 大厂Offer拿到手软啊!隔壁都馋哭了
  18. TextView的居中显示、多行显示、单行显示且显示不完用省略号替代...
  19. python 人民币兑美元汇率代码_Python获取美元人民币实时汇率
  20. 斯坦福大学秋季课程《深度学习理论》STATS 385开讲

热门文章

  1. python绘图中文_Python绘图实现显示中文
  2. linux微信电脑版登录不了,默认Windows与Deepin系统下的微信电脑版目录
  3. KEIL5 各个版本编译器的下载
  4. 强化学习之AC、A2C和A3C
  5. 强化学习经典算法笔记(十七):A3C算法的PyTorch实现
  6. premiere软件的使用(快速入门,迅速了解常用功能、常用快捷键、常用插件)——wsdchong
  7. PR曲线与ROC曲线绘制
  8. python编程视频剪辑_MoviePy常用剪辑类及Python视频剪辑自动化
  9. Windows电脑桌面云便签自由拖动排序如何调整便签的顺序?
  10. 常见五大开源网络监控软件测评分析