Android 图解自定义车速表
来源:互联网 发布:福建网络服务公司 编辑:程序博客网 时间:2024/05/09 13:38
简单讲述绘制圆弧、渐变圆、时速表刻度、文字和时速指针
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方法限定控件的宽高
@Overrideprotected 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*mDensity mShader = 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 - 文字宽度/2float 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 - 文字宽度/2float 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线程中调用,进行重绘 else postInvalidate();//在子线程中调用,进行重绘}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() { @Override public 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 图解自定义车速表
- 求车速
- 求车速
- 求车速
- 求车速
- 求车速
- 求车速
- 车速问题
- Android:图解classloader加载class的流程及自定义ClassLoader
- 图解Android
- 图解Android
- android图解
- 图解Android
- 图解Android
- 图解Android
- 图解Android
- 图解Android
- 图解Android
- springjdbc
- hdu 5934 Bomb
- 删除文件
- JUnit4 wiki翻译
- springboot+freemarker实现简单添加员工
- Android 图解自定义车速表
- Codeforces Round #435 (Div. 2)B. Mahmoud and Ehab and the bipartiteness(补)
- 常见算法复杂度
- 递归与迭代
- 多类别逻辑回归(Multinomial Logistic Regression)
- 文件操作专题
- UBUNTU上打字练习软件
- js排序算法详解-选择排序
- Codeforces 856C [DP]