1、概述

最近学习自定义View,趁着周末做了一个仪表盘练练手,效果还可以,在此分享一下先上效果图(截图有点不清晰,凑合着看下吧)

项目在我的github上https://github.com/xsfelvis/PanelView

有图才能有真相,下面简要说一下如何实现的

2、实现

【分析有哪些属性需要】

在values/attr文件中进行声明,这些属性都是可以在xml中进行使用的,实现定制的,比如unit单位属性

  1. <resources>
  2.  
  3. <declare-styleable name="PanelView">
  4. <attr name="arcColor" format="color"/>
  5. <attr name="arcWidth" format="dimension"/>
  6. <attr name="secArcWidth" format="dimension"/>
  7. <attr name="android:text"/>
  8. <attr name="tikeCount" format="integer"/>
  9. <attr name="pointerColor" format="color"/>
  10. <attr name="Unit" format="string"/>
  11. <attr name="android:textSize"/>
  12. <attr name="AcrStartColor" format="color"/>
  13. <attr name="AcrEndColor" format="color"/>
  14. <attr name="textColor" format="color"/>
  15.  
  16. </declare-styleable>
  17.  
  18. </resources>

然后为了结构上更加工整,推荐单独写一个文件来处理这些属性,最好不要和自定义控件混在一起

这里在PanelViewAttr.java中做了处理

  1. package PanelView;
  2.  
  3. import android.content.Context;
  4. import android.content.res.TypedArray;
  5. import android.util.AttributeSet;
  6.  
  7. import com.xsf.panelview.R;
  8.  
  9. import util.PxUtils;
  10.  
  11. /**
  12. * Created by hzxushangfei on 2016/1/23.
  13. */
  14. public class PanelViewAttr {
  15. private int mArcColor;
  16. private int mPointerColor;
  17. private int mTikeCount;
  18. private int mTextSize;
  19. private String mText = "";
  20. private int arcwidth;
  21. private int mScendArcWidth;
  22. private String unit;//单位
  23. private int acrStartColor;
  24. private int acrEndColor;
  25. private int textColor;
  26.  
  27. public PanelViewAttr(Context context, AttributeSet attrs, int defStyleAttr) {
  28. TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.PanelView, defStyleAttr, 0);
  29. mArcColor = ta.getColor(R.styleable.PanelView_arcColor, context.getResources().getColor(R.color.colorPrimaryDark));
  30. mPointerColor = ta.getColor(R.styleable.PanelView_pointerColor, context.getResources().getColor(R.color.PointerColor));
  31. mTikeCount = ta.getInt(R.styleable.PanelView_tikeCount, 12);
  32. mTextSize = ta.getDimensionPixelSize(PxUtils.spToPx(R.styleable.PanelView_android_textSize, context), 24);
  33. mText = ta.getString(R.styleable.PanelView_android_text);
  34. arcwidth = ta.getInt(R.styleable.PanelView_arcWidth, 3);
  35. mScendArcWidth = ta.getInt(R.styleable.PanelView_secArcWidth, 50);
  36. unit = ta.getString(R.styleable.PanelView_Unit);
  37. acrStartColor = ta.getColor(R.styleable.PanelView_AcrStartColor, context.getResources().getColor(R.color.GREEN));
  38. acrEndColor = ta.getColor(R.styleable.PanelView_AcrEndColor, context.getResources().getColor(R.color.RED));
  39. textColor = ta.getColor(R.styleable.PanelView_textColor, context.getResources().getColor(R.color.Yellow));
  40. ta.recycle();
  41. }
  42.  
  43. public int getAcrEndColor() {
  44. return acrEndColor;
  45. }
  46.  
  47. public int getAcrStartColor() {
  48. return acrStartColor;
  49. }
  50.  
  51. public int getArcwidth() {
  52. return arcwidth;
  53. }
  54.  
  55. public int getmArcColor() {
  56. return mArcColor;
  57. }
  58.  
  59. public int getmPointerColor() {
  60. return mPointerColor;
  61. }
  62.  
  63. public int getmTikeCount() {
  64. return mTikeCount;
  65. }
  66.  
  67. public int getmTextSize() {
  68. return mTextSize;
  69. }
  70.  
  71. public String getmText() {
  72. return mText;
  73. }
  74.  
  75. public int getmScendArcWidth() {
  76. return mScendArcWidth;
  77. }
  78.  
  79. public String getUnit() {
  80. return unit;
  81. }
  82. public int getTextColor() {
  83. return textColor;
  84. }
  85.  
  86. }

再然后在自定义view实现类 PanelView.java中获取

  1. panelViewattr = new PanelViewAttr(context, attrs, defStyleAttr);

通过get方法获取

  1. mArcColor = panelViewattr.getmArcColor();
  2. mPointerColor = panelViewattr.getmPointerColor();
  3. mTikeCount = panelViewattr.getmTikeCount();
  4. mTextSize = panelViewattr.getmTextSize();
  5. mTextColor = panelViewattr.getTextColor();
  6. mText = panelViewattr.getmText();
  7. mArcWidth = panelViewattr.getArcwidth();
  8. mScendArcWidth = panelViewattr.getmScendArcWidth();
  9. unit = panelViewattr.getUnit();
  10. acrStartColor = panelViewattr.getAcrStartColor();
  11. acrEndColor = panelViewattr.getAcrEndColor();

OK,这些都是小儿科,主要看onMeasure和OnDraw()方法

这里主要对exactly做了处理,其余的按照统一处理onMeasure()方法如下,推荐用一个方法来处理measure,不要把宽和高都写一遍,这样代码看起来会有点冗杂

  1. @Override
  2. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  3. int realWidth = startMeasure(widthMeasureSpec);
  4. int realHeight = startMeasure(heightMeasureSpec);
  5.  
  6. setMeasuredDimension(realWidth, realHeight);
  7. }
  8.  
  9. private int startMeasure(int msSpec) {
  10. int result = 0;
  11. int mode = MeasureSpec.getMode(msSpec);
  12. int size = MeasureSpec.getSize(msSpec);
  13. if (mode == MeasureSpec.EXACTLY) {
  14. result = size;
  15. } else {
  16. result = PxUtils.dpToPx(200, mContext);
  17. }
  18. //Log.d("xsf", "startMeasure " + result);
  19. return result;
  20. }

onMeasure之后决定了大小,重头戏在onDraw,直接看onraw方法

  1. @Override
  2. protected void onDraw(Canvas canvas) {
  3. super.onDraw(canvas);
  4. float percent = mPercent / 100f;
  5.  
  6. //最外面线条
  7. drawOutAcr(canvas);
  8. //绘制刻度
  9. drawerNum(canvas);
  10. //绘制粗圆弧
  11. drawInArc(canvas, percent);
  12. //绘制中间小圆和圆环
  13. drawInPoint(canvas);
  14. //绘制指针
  15. drawerPointer(canvas, percent);
  16. //绘制矩形和文字
  17. drawerRecAndText(canvas, percent);
  18.  
  19. }

这样写是不是显得逻辑非常清楚,不要把细节都放在ondraw方法中,用一个个函数封装是最好不过的了

首先 drawOutAcr(canvas)

画最外面的弧

  1. private void drawOutAcr(Canvas canvas) {
  2. //最外面线条
  3. rectF1 = new RectF(mArcWidth, mArcWidth, getWidth() - mArcWidth, getHeight() - mArcWidth);
  4. canvas.drawArc(rectF1, START_ARC, DURING_ARC, false, paintOuter_Arc);
  5. }

主要使用到canvas.drawArc方法,没啥好说的

执行完你会看到一个弧

然后是画刻度drawerNum(canvas);

这个函数开始画刻度和数字

  1. private void drawerNum(Canvas canvas) {
  2. canvas.save(); //记录画布状态
  3. canvas.rotate(-(180 - START_ARC + 90), getWidth() / 2, getHeight() / 2);
  4. float rAngle = DURING_ARC / mTikeCount;
  5. for (int i = 0; i < mTikeCount + 1; i++) {
  6. canvas.save(); //记录画布状态
  7. canvas.rotate(rAngle * i, getWidth() / 2, getHeight() / 2);
  8. canvas.drawLine(getWidth() / 2, mArcWidth, getWidth() / 2, 20, paintOuter_Arc);//画刻度线
  9. canvas.drawText("" + i * 10, getWidth() / 2 - mArcWidth * 2, 40, paintouter_Num);//画刻度
  10. canvas.restore();
  11. }
  12. canvas.restore();
  13. }

要点:canvas.save 和canvas.restore来记录和恢复画布的状态

结合canvas.ratate旋转画布,画布旋转固定角度,画笔此时不需要变化,大大方便,这个技巧需要掌握

此时画出

然后执行drawInArc(canvas, percent);画粗圆弧

  1. private void drawInArc(Canvas canvas, float percent) {
  2. rectF2 = new RectF(mArcWidth + OFFSET, mArcWidth + OFFSET, getWidth() - mArcWidth - OFFSET, getHeight() - mArcWidth - OFFSET);
  3.  
  4. canvas.drawArc(rectF2, START_ARC, DURING_ARC, false, paintInerArc);
  5.  
  6. rectF3 = new RectF(mArcWidth + OFFSET, mArcWidth + OFFSET, getWidth() - mArcWidth - OFFSET, getHeight() - mArcWidth - OFFSET);
  7. shader = new LinearGradient(mArcWidth + OFFSET, mArcWidth + OFFSET,
  8. getWidth() - mArcWidth - OFFSET, getHeight() - mArcWidth - OFFSET, acrStartColor, acrEndColor, Shader.TileMode.REPEAT);
  9. paintInerArc_tranform.setShader(shader);
  10. canvas.drawArc(rectF3, START_ARC, <span style="color:#000099;">percent * DURING_ARC</span>, false, paintInerArc_tranform);
  11. }

这个涉及到了粗圆弧颜色状态的变化,首先画一个白色的圆弧,画笔设置粗一点,为白色

然后使用shader来变色,在相同的圆弧上根据百分比再覆盖一层颜色,我们画弧是从150度(START_ARC)为开始位置,持续了240度(DURING_ARC)

此时状态为

然后开始画圆环和圆点这个比较简单,注意下圆环使用空心画笔,圆点使用实心画笔即可

  1. private void drawInPoint(Canvas canvas) {
  2. canvas.drawCircle(getWidth() / 2, getHeight() / 2, mMinRingRadius, paintOuter_Arc);//中心小圆环
  3. canvas.drawCircle(getWidth() / 2, getHeight() / 2, mMinCircleRadius, paint_centerPoint_Pointer);//中心圆点
  4. }

圆弧颜色变化解决了,下面来解决指针跟随百分比变化

percent在0-1之间,实现-120- +120变化(DURING_ARC=240),那么转化成数学关系就是,变化角度angle = DURING_ARC*(percent-0.5),ok表达式搞定就好搞了

  1. private void drawerPointer(Canvas canvas, float percent) {
  2. canvas.save();
  3. float angel = DURING_ARC * (percent - 0.5f);
  4. canvas.rotate(angel, getWidth() / 2, getHeight() / 2);//指针与外弧边缘持平
  5. paint_centerPoint_Pointer.setStrokeWidth(mArcWidth);
  6. canvas.drawLine(getWidth() / 2, getHeight() / 2, getWidth() / 2, getHeight() / 2 - mArcWidth * 2 - OFFSET - mScendArcWidth, paint_centerPoint_Pointer);
  7. canvas.restore();
  8. }

根据百分比变化位置的指针也画出来了

最后画画文字和单位

  1. private void drawerRecAndText(Canvas canvas, float percent) {
  2. float length = 0;
  3. paint_text.setTextSize(mTextSize);
  4.  
  5. length = paint_text.measureText(mText);
  6. canvas.drawText(mText, getWidth() / 2 - length / 2, (float) (getHeight() / 2 * (1 + Math.sqrt(2) / 3)), paint_text);
  7.  
  8. paint_text.setTextSize(mTextSize * 1.5f);
  9. speed = StringUtil.floatFormat(120 * percent) +<span style="color:#336666;"> unit</span>;
  10. length = paint_text.measureText(speed);
  11.  
  12. canvas.drawText(speed, getWidth() / 2 - length / 2, (float) (getHeight() / 2 * (1 + Math.sqrt(2) / 2)), paint_text);
  13. }

为了使文字画在中间需要使用path里面的measureText的方法,然后为了文字和速度字体有层次感,这里设置为文字的1.5倍大小

ok很简答吧!

自定义仪表盘PaneView的更多相关文章

  1. IOS自定义仪表盘

      登录|注册     周海锋 的专栏 Objective-C/Cocos2d/Cocos2d-x/Php/JS       目录视图 摘要视图 订阅 2016软考项目经理实战班    学院周年礼-顶 ...

  2. Android 中自定义仪表盘

    如图: 自定义属性 values文件下添加 attrs.xml文件 <?xml version="1.0" encoding="utf-8"?> & ...

  3. WPF自定义仪表盘控件

    闲来无事,分享一个仪表盘 源码: 直接复制代码即可运行,=.=! <Window x:Class="TGP.InstrumentationDemo.MainWindow" x ...

  4. 黄聪:定制化WordPress后台自定义仪表盘

    WordPress作为一博客管理系统,相对来说已经相当简洁了,对用户也十分友好,新手也极易上手. 仪表盘是我们登陆WordPress后看到的后台界面,映入眼帘的是各种各样的信息,如WordPress ...

  5. Cloud Insight 仪表盘上线 | 全面监控 Redis

    OneAPM 作为应用性能领域的新兴领军企业,近期发布了重量级新产品-- Cloud Insight 数据管理平台,用它能够监控所有基础组件,并通过 tag 标签对数据进行管理. 近日,Cloud I ...

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

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

  7. ELK学习实验009:安装kibana的仪表盘

    一 metricbeat仪表盘 1.1 安装metricbeat仪表盘 可以将metricbeat数据在kibana中展示 [root@node4 ~]# cd /usr/local/metricbe ...

  8. CODING 仪表盘功能正式推出,实现工作数据可视化!

    CODING 仪表盘功能现已正式推出!该功能旨在用一张张统计卡片的形式,统计并展示使用 CODING 中所产生的数据.这意味着无需额外的设置,就可以收集归纳宝贵的工作数据并予之量化分析.这些海量的数据 ...

  9. 简单4步,利用Prometheus Operator实现自定义指标监控

    本文来自Rancher Labs 在过去的文章中,我们花了相当大的篇幅来聊关于监控的话题.这是因为当你正在管理Kubernetes集群时,一切都会以极快的速度发生变化.因此有一个工具来监控集群的健康状 ...

随机推荐

  1. Dynamics CRM 部署NLB后使用群集名称访问弹验证框验证不过的解决方法

    自上次部署NLB到现在已有段时间了,今天部署完后遇到了个问题,上次也遇到过但忘记了,本篇作为对该问题的一个记录,部署文档:https://blogs.msdn.microsoft.com/niran_ ...

  2. Android Studio精彩案例(一)《ActionBar和 ViewPager版仿网易新闻客户端》

    转载本专栏文章,请注明出处,尊重原创 .文章博客地址:道龙的博客 为了能更好的分享高质量的文章,所以开设了此专栏.文章代码都以Android Studio亲测运行,读者朋友可在后面直接下载源码.该专栏 ...

  3. Linux--FTP和MAIL服务器

     1) FTP协议 FTP(FileTransfer Protocol,文件传输协议)用于管理计算机之间的文件传送.FTP 是Internet 上使用非常广泛的一种通讯协议,它是由支持Intern ...

  4. UE4成批处理透明材质

    项目中需要控制成批的物体的透明度,但是默认的时候他又不能是透明的,对,项目的要求就这么诡异. 然而却没有找到设置材质的BlendMode的功能,于是只有换了一种办法,物体需要透明时更换为透明材质,默认 ...

  5. XML命名规则

    XML = Extensible Markup Language,可扩展标记语言 XML 标签对大小写敏感,XML 标签对大小写敏感.在XML 中,标签 <Letter> 与标签 < ...

  6. API创建员工

    DECLARE lc_employee_number PER_ALL_PEOPLE_F.EMPLOYEE_NUMBER%TYPE := 'PRAJ_01'; ln_person_id PER_ALL_ ...

  7. IT女孩特不烦恼---九月实习总结

    对着岁月落笔,画出一场清风,那是最真的笑容 一溜烟的功夫,小编来实习Android已经四个月了,从刚开始的电商项目到现在的车段子项目,小编渐渐对这个曾经陌生的名字慢慢扭转变成熟悉的面孔,四个月的时间, ...

  8. reactor线程阻塞引起故障

    大致线程模型: jstack打印JVM堆栈,可以看到reactor线程阻塞了,导致它对应的前端连接无法使用.阻塞在了oracle驱动rollback动作,这里其实是因为oracle驱动为了保证串行请求 ...

  9. 利用Camera和Matrix实现有趣的卡片效果

    这篇文章主要讲解一个翻转切换内容的卡片效果,主要利用Camera和Matrix来实现,主要是为了加深对Camera和Matrix的理解,如果对Camera和Matrix不清楚地童鞋可以看我的上篇文章: ...

  10. UNIX网络编程——非阻塞式I/O(套接字)

    套接字的默认状态是阻塞的.这就意味着当发出一个不能立即完成的套接字调用时,其进程将被投入睡眠,等待相应的操作完成.可能阻塞的套接字调用可分为以下4类: (1)输入操作,包括read,readv,rec ...