Android 图解自定义车速表

2023-11-01 22:50

本文主要是介绍Android 图解自定义车速表,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

简单讲述绘制圆弧、渐变圆、时速表刻度、文字和时速指针

1.前言:下图来自于度娘,截取一部分来绘制,其他的内容大同小异;而动图为所实现的效果图。
效果图

完成图

2.需求分析:如上第一张图,这些参数可能经常被变动,所以把这些做成自定义属性,方便后面修改。

3.自定义属性的定义: 在values目录下新建attr.xml

<?xml version="1.0" encoding="utf-8"?>
<resources><!--1.自定义属性--><declare-styleable name="CarBoardView"><attr name="outRingColor" format="color"/><attr name="innerRingColor" format="color"/><attr name="speedColor" format="color"/><attr name="indicatorColor" format="color"/><attr name="outRingRadius" format="float"/><attr name="innerRingRadius" format="float"/><attr name="outSpeedSize" format="float"/><attr name="innerSpeedSize" format="float"/><attr name="speedUnitSize" format="float"/></declare-styleable></resources>

如果不知道格式可参考android源码定义的:sdk\platforms\android-23\data\res\values

4.自定义属性的获取: TypedArray是通过上下文context获取的,要注意用完要调用recycle()方法进行回收

public CarBoardView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);//屏幕密度,为了适配各种不同像素的手机mDensity = context.getResources().getDisplayMetrics().density;TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CarBoardView,defStyleAttr, defStyleRes);mOutRingColor = a.getColor(R.styleable.CarBoardView_outRingColor, Color.BLUE);mInnerRingColor = a.getColor(R.styleable.CarBoardView_innerRingColor, Color.BLUE);mSpeedColor = a.getColor(R.styleable.CarBoardView_speedColor, Color.WHITE);mIndicatorColor = a.getColor(R.styleable.CarBoardView_indicatorColor, Color.RED);mOutRingRadius = a.getFloat(R.styleable.CarBoardView_outRingRadius, 100) * mDensity;mInnerRingRadius = a.getFloat(R.styleable.CarBoardView_innerRingRadius, 50) * mDensity;mOutSpeedSize = a.getFloat(R.styleable.CarBoardView_outSpeedSize, 13) * mDensity;mInnerSpeedSize = a.getFloat(R.styleable.CarBoardView_innerSpeedSize, 18) * mDensity;mSpeedUnitSize = a.getFloat(R.styleable.CarBoardView_speedUnitSize, 13) * mDensity;a.recycle();mHeight = mWidth = (int) (mOutRingRadius*2 + 10*mDensity);//先定义宽高,可以先初始化画笔等initTools();
}

5.测量控件大小:通过重新onMeasure方法,调用setMeasuredDimension方法限定控件的宽高

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {mStartMarkX = (float) (mWidth/2 - mOutRingRadius*Math.sin(Math.PI*45/180) + 5*mDensity);mStartMarkY = (float) (mWidth/2 + mOutRingRadius*Math.cos(Math.PI*45/180) + 5*mDensity);mMarkAngle = 270 / 15f;Log.i(TAG, "onMeasure mWidth: " + mWidth + ",mHeight: " + mHeight + " ,mMarkAngle: "+mMarkAngle);setMeasuredDimension(mWidth, mHeight);//限定本view的宽高要一致
}

6.初始化画笔等:初始化画笔等工具

private void initTools() {Log.i(TAG,"initTools");mPaint = new Paint();mOutRingRectF = new RectF(5*mDensity,5*mDensity,mWidth-5*mDensity,mHeight-5*mDensity);//距离边界5*mDensitymShader = new RadialGradient(mWidth/2,mHeight/2,mInnerRingRadius, //三个数字分别表示,圆心的X、Y轴坐标以及半径new int[]{mInnerRingColor,0xFF53C0E7, 0xFF2062E8}, //这里是用来设置颜色值的,在这个int数组内可以有N组Color值new float[]{0.6f,0.8f,1f},Shader.TileMode.MIRROR);//0.6f,0.8f,1f透明度是指从里到外的渐变;而且注意要跟上面Color数据长度相等mTextPaint = new Paint();mBound = new Rect();
}

7.onDraw绘制过程:在onDraw里不要尽量不要创建对象,因为频繁的绘制会不断的创建对象,然而gc会不断的回收,会降低性能

(1)画外圆弧:drawArc方法,参1:用来定义形状和大小;参2:弧的起始角度(解析下图);参3:旋转过的角度;参4:是否闭合(即与圆中心是否相连);参5:画笔

这里写图片描述

mPaint.setStrokeWidth(2*mDensity);
mPaint.setColor(mOutRingColor);
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawArc(mOutRingRectF,120,300,false, mPaint);

(2)画渐变内圆:mPaint.setShader(mShader)给画笔设置圆环的渐变效果,上面有介绍;drawCircle方法,参1:圆中心x坐标;参2:圆中心y坐标;参3:半径;参4:画笔

mPaint.setStrokeWidth(7*mDensity);
mPaint.setShader(mShader);
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(mWidth/2,mHeight/2,mInnerRingRadius-5*mDensity, mPaint);//这里绘制出来一个比渐变圆略小的圆,并且覆盖到渐变圆上

(3)画外圆刻度:画出第一条线,然后以圆心为旋转点,经过n°画出16个刻度;drawLine方法:参1:起始x坐标;参2:起始y坐标;参3:终点x坐标;参4:终点y坐标;参5:画笔

这里写图片描述

canvas.save(); //这时候保存的是画布没旋转之前的状态
mPaint.reset();//重置画笔
float degreeLength = 10*mDensity;
mPaint.setColor(mOutRingColor);
mPaint.setStrokeWidth(2*mDensity);
mPaint.setAntiAlias(true);
for(int i=0;i<16;i++){canvas.drawLine(mStartMarkX, mStartMarkY, mStartMarkX+degreeLength, mStartMarkY-degreeLength, mPaint);canvas.rotate(mMarkAngle, mWidth/2, mHeight/2);//旋转角度,x支点,y支点(就是环绕支点移动)
}

(4)画外圆时速(数字):这里得要慢慢调,比较麻烦,画字体是从左下标开始的

canvas.restore();//还原状态(还原上一个save的状态),即将旋转过的画布重置
mTextPaint.setTypeface(Typeface.DEFAULT_BOLD);
mTextPaint.setColor(mSpeedColor);
mTextPaint.setTextSize(mOutSpeedSize);
mTextPaint.setStyle(Paint.Style.FILL);
mTextPaint.setAntiAlias(true);
float x;
float y;
for(int i=0;i<16;i++){x = (float) (mWidth/2 - (mOutRingRadius-degreeLength)*Math.cos((Math.PI*45-Math.PI*mMarkAngle*i)/180));y = (float) (mWidth/2 + (mOutRingRadius-degreeLength)*Math.sin((Math.PI*45-Math.PI*mMarkAngle*i)/180));switch (i){case 0:case 1:case 2:x = x+(i+1)*mDensity*(i==0?4:(i==1?3:2));y = y+(i+1)*mDensity*4;break;case 3:case 4:case 5:x = x-i*mDensity/(i==3?-1:2);y = y+i*mDensity*(i==5?3:4);break;case 6:case 7:case 8:x = x-i*mDensity*2;y = (float) (y+i*mDensity*(i==6?3:(i==7?2:1.5)));break;case 9:case 10:case 11:x = x-i*mDensity*2;y = (float) (y+i*mDensity/(i==9?1:(i==10?1.5:3)));break;case 12:case 13:case 14:x = (float) (x-i*mDensity*(i==12?2:1.5));y = y-i*mDensity/2;break;case 15:x = x-i*mDensity;y = y-i*mDensity/2;break;}canvas.drawText(String.valueOf(30*i),x, y,mTextPaint);
}

(5)画内圆时速:使用画笔设置getTextBounds方法,实现根据字体长度而居中显示

String text = String.valueOf(mSpeed);
mTextPaint.setTextSize(mInnerSpeedSize);
mTextPaint.setColor(mSpeedColor);
mTextPaint.getTextBounds(text, 0, text.length(), mBound);
float startX1 = mWidth/2 - mBound.width()/2;//控件宽度/2 - 文字宽度/2
float startY1 = mHeight/2 + mBound.height()/2-mInnerSpeedSize;//控件高度/2 + 文字高度/2,绘制文字从文字左下角开始,因此"+"
canvas.drawText(text, startX1, startY1, mTextPaint);// 绘制文字

(6)画内圆速度单位:跟上面一样原理

String text2 = "km·h";
mTextPaint.setTextSize(mSpeedUnitSize);
mTextPaint.setColor(mOutRingColor);
mTextPaint.getTextBounds(text2, 0, text2.length(), mBound);
float startX2 = mWidth/2 - mBound.width()/2;//控件宽度/2 - 文字宽度/2
float startY2 = mHeight/2 + mBound.height()/2;//控件高度/2 + 文字高度/2,绘制文字从文字左下角开始,因此"+"
canvas.drawText(text2,startX2, startY2,mTextPaint);

(7)画时速指针:其实就是画一个封闭的三角形

这里写图片描述

Path path = new Path();//这里创建Path对象为了保证每次绘制都是新一条path(并且显示出来只有一条)
mPaint.setStrokeWidth(2*mDensity);
mPaint.setColor(mIndicatorColor);
mPaint.setStyle(Paint.Style.FILL);
int m = 7;
float startX = (float) (mWidth/2 - mOutRingRadius*Math.cos((Math.PI*45 - Math.PI*(mSpeed-m)/450*270)/180));
float startY = (float) (mWidth/2 + mOutRingRadius*Math.sin((Math.PI*45 - Math.PI*(mSpeed-m)/450*270)/180));
float endX1 = (float) (mWidth/2 - (mInnerRingRadius-m*mDensity)*Math.cos((Math.PI*45-Math.PI*(mSpeed+m)/450*270)/180));
float endY1 = (float) (mWidth/2 + (mInnerRingRadius-m*mDensity)*Math.sin((Math.PI*45-Math.PI*(mSpeed+m)/450*270)/180));
float endX2 = (float) (mWidth/2 - (mInnerRingRadius-m*mDensity)*Math.cos((Math.PI*45-Math.PI*(mSpeed-m)/450*270)/180));
float endY2 = (float) (mWidth/2 + (mInnerRingRadius-m*mDensity)*Math.sin((Math.PI*45-Math.PI*(mSpeed-m)/450*270)/180));
path.moveTo(startX, startY);// 此点为多边形的起点,指针的尖的一端
path.lineTo(endX1, endY1);
path.lineTo(endX2, endY2);
path.close(); // 使这些点构成封闭的多边形
canvas.drawPath(path, mPaint);

8.设置对外接口:根据外界传递的速度进行重绘view,重新执行onDraw里面的代码

public void setSpeed(int speed){mSpeed = speed;if(isMainThread())invalidate();//在UI线程中调用,进行重绘elsepostInvalidate();//在子线程中调用,进行重绘
}public boolean isMainThread() {return Looper.getMainLooper() == Looper.myLooper();
}

9.进行测试:在Activity进行测试

final CarBoardView cbv = (CarBoardView) findViewById(R.id.cbv);
cbv.setSpeed(0);
new Thread(new Runnable() {@Overridepublic void run() {int i=0;try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}while (i<=450){cbv.setSpeed(i);i = i+i%15 +1;
//             Log.i(TAG,"====i: "+i);try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}}}
}).start();

**10.后语:**android东西太多了,做的时候肯定有一些没遇到过,我通常是会看看给类会提供什么方法供我达到效果;比如在做圆环渐变效果时,看到有提供setShader方法,初步估计能达到效果;然后百度了一下,看到有个子类RadialGradient能很好的达到效果,那博主看没有注意这可以支持多维数组的,而他认为只能实现两组,也就是两种渐变的效果;欢迎大家过来交流,共同进步。

(1)这自定义view只能外界设置进来,另外一篇是在view操作,然后外界来监听:http://blog.csdn.net/github_38117599/article/details/70244518

(2)demo的地址:https://github.com/xhunmon/CarBoredView

这篇关于Android 图解自定义车速表的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/326326

相关文章

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

Android里面的Service种类以及启动方式

《Android里面的Service种类以及启动方式》Android中的Service分为前台服务和后台服务,前台服务需要亮身份牌并显示通知,后台服务则有启动方式选择,包括startService和b... 目录一句话总结:一、Service 的两种类型:1. 前台服务(必须亮身份牌)2. 后台服务(偷偷干

CSS自定义浏览器滚动条样式完整代码

《CSS自定义浏览器滚动条样式完整代码》:本文主要介绍了如何使用CSS自定义浏览器滚动条的样式,包括隐藏滚动条的角落、设置滚动条的基本样式、轨道样式和滑块样式,并提供了完整的CSS代码示例,通过这些技巧,你可以为你的网站添加个性化的滚动条样式,从而提升用户体验,详细内容请阅读本文,希望能对你有所帮助...

Android kotlin语言实现删除文件的解决方案

《Androidkotlin语言实现删除文件的解决方案》:本文主要介绍Androidkotlin语言实现删除文件的解决方案,在项目开发过程中,尤其是需要跨平台协作的项目,那么删除用户指定的文件的... 目录一、前言二、适用环境三、模板内容1.权限申请2.Activity中的模板一、前言在项目开发过程中,尤

龙蜥操作系统Anolis OS-23.x安装配置图解教程(保姆级)

《龙蜥操作系统AnolisOS-23.x安装配置图解教程(保姆级)》:本文主要介绍了安装和配置AnolisOS23.2系统,包括分区、软件选择、设置root密码、网络配置、主机名设置和禁用SELinux的步骤,详细内容请阅读本文,希望能对你有所帮助... ‌AnolisOS‌是由阿里云推出的开源操作系统,旨

Android数据库Room的实际使用过程总结

《Android数据库Room的实际使用过程总结》这篇文章主要给大家介绍了关于Android数据库Room的实际使用过程,详细介绍了如何创建实体类、数据访问对象(DAO)和数据库抽象类,需要的朋友可以... 目录前言一、Room的基本使用1.项目配置2.创建实体类(Entity)3.创建数据访问对象(DAO

SpringBoot 自定义消息转换器使用详解

《SpringBoot自定义消息转换器使用详解》本文详细介绍了SpringBoot消息转换器的知识,并通过案例操作演示了如何进行自定义消息转换器的定制开发和使用,感兴趣的朋友一起看看吧... 目录一、前言二、SpringBoot 内容协商介绍2.1 什么是内容协商2.2 内容协商机制深入理解2.2.1 内容

Android WebView的加载超时处理方案

《AndroidWebView的加载超时处理方案》在Android开发中,WebView是一个常用的组件,用于在应用中嵌入网页,然而,当网络状况不佳或页面加载过慢时,用户可能会遇到加载超时的问题,本... 目录引言一、WebView加载超时的原因二、加载超时处理方案1. 使用Handler和Timer进行超

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo