一步步实现精美的钟表界面

先来效果:

下面就直接进入正题吧:

1. 创建 WatchBoard (自定义View) 继承View,实现构造方法。

2. 添加一些必要的属性,并且自定义资源文件,书写代码获取属性。

一些需要的属性,从实例图可以看出我们需要的可以定制和必须的属性主要有以下几个:

private float mRadius; //外圆半径
private float mPadding; //边距
private float mTextSize; //文字大小
private float mHourPointWidth; //时针宽度
private float mMinutePointWidth; //分针宽度
private float mSecondPointWidth; //秒针宽度
private int mPointRadius; // 指针圆角
private float mPointEndLength; //指针末尾的长度
private int mColorLong; //长线的颜色
private int mColorShort; //短线的颜色
private int mHourPointColor; //时针的颜色
private int mMinutePointColor; //分针的颜色
private int mSecondPointColor; //秒针的颜色
private Paint mPaint; //画笔

关于各个属性的作用也写一下,以前看别人的自定义View就有的属性根本不知道要用来干啥,难以理解:

定义资源文件:在value文件下新建 watch_board_attr.xml 文件,内容如下:


构造方法中获取属性并且设置默认值,添加异常情况的处理(一旦出现异常,使用全部默认值).


其中用到的尺寸转换方法:


3. 初始化画笔

//画笔初始化
private void init() {    mPaint = new Paint();    mPaint.setAntiAlias(true);    mPaint.setDither(true); }

4. 由于表盘始终显示是圆形的,要做到图形一直在view的中间很简单,但是那样就会浪费很多的空间,于是我们应该重写 onMeasure 方法,使得表盘始终只占用一个正方形的空间,但是处理的前提是用户一定会给一个确定的值,不管是宽度还是高度或者两者都是.

处理思路:

  • 当宽高均为 wrap_content 的时候抛出异常,因为这样的操作对于这个组件来说是不合理的

  • 给初始化宽度设置一个很大的值,当宽度或者高度确定时取最小值,因为宽高必定有一个为确定值,所以这样过后会得到宽高的最小值

代码如下:

自定义的异常:


onMeasure方法:


现在的效果如下:(宽高均为 match_parent 的时候也仍然只占用一个正方形)

这样做的原因是减少空间的浪费,主要还是避免下面的这种情况(设置宽或高为 match_parent 时占满全屏,影响其他组件的显示.)

5. 获取表盘圆的半径值与尾部长度值

获取值应该在测量完成之后,所以在 onSizeChange 里面获取:


6. 接下来就是最重要的绘制阶段了

主要分为几个阶段:

  • 绘制外圆表盘

  • 绘制刻度与时间标示

  • 绘制指针

首先绘制表盘

为减少计算量,首先将 canvas 的坐标原点移动到中心位置,这一步的操作如图所示:

@Overrideprotected void onDraw(Canvas canvas) {
    canvas.save();
    canvas.translate(getWidth() / 2, getHeight() / 2);
   ...    canvas.restore(); }

已获取到半径了,那直接绘制圆形:


这一步效果如下:

接下来是绘制刻度与文字

从实例图上可以看出,一共有60个刻度,两个刻度之间的角度是,其中包含12个整点刻度.

绘制的时候我们希望的当然是直接在X轴或者Y轴上绘制线条,但是当前的x,y相对于原点来说都是水平,垂直的,那么我们就可以想到将坐标系每次旋转6°进行绘制,一共旋转60次,并且每次都是在x轴或者y轴上绘制.

设刻度长度 mLineWidth,选定Y轴绘制线条,过程如下:

对 60个刻度 进行判断,整点和非整点刻度设置不同的长度,颜色,宽度,绘制一个之后画布旋转 6°,即可完成所有刻度的绘制,代码如下:


现在的效果如下:

接下来要绘制文字,由于只有整点才有文字,所以文字的绘制就放到整点绘制的if里面.

首先获取要显示的文字内容:

String text = ((i / 5) == 0 ? 12 : (i / 5)) + "";

不难理解,为 0~60 ,而第一个Y轴上绘制的应该是12点,其他的只要对5作取余数就可得到.

由实例图可以看到文字都是垂直方向上的,所以绘制文字的时候应该将画布中心移动到刻度后面,并且旋转的角度与绘制刻度旋转的角度相反,当然别忘了用 canvas.save()  canvas.restore().这么说可能有点难理解,看图吧:

这有几个数据需要处理

  • 已经旋转过的坐标中心,要移动到刻度后面,那么他移动后的Y坐标因该是多少?

  • 要将文字绘制在垂直方向,逆时针方向偏转的角度是多少?

  • 文字的高度如何计算?

先处理一下这几个数据:

上图已经标记清楚了,坐标原点移动到刻度后面,移动后的Y轴的值如下:

Y坐标 = -mRadius + mLineWidth(刻度长度)+文字高度+文字与刻度的偏移量

偏移量我们自己设计,暂且设为 5dp,而刻度长度已经确定了,那么只剩下文字的高度,由如下方法获得:


最终的移动后的Y坐标值为:

-mRadius + DptoPx(5) + lineWidth + (textBound.bottom - textBound.top))

数据都有了,接下来就是绘制文字了.绘制文字目前还需要绘制文字的起始X,Y坐标,注意其中的Y坐标的基线的坐标.有不懂的同学建议看着片博客,后部分有关于绘制文字的详细内容:

Android仿京东首页轮播文字(又名垂直跑马灯)

http://blog.csdn.net/qq_26971803/article/details/51537129

绘制文字的示意图如下:

图上说的已将很明白了,那么就开始绘制文字了


效果图:

由图可以看出文字和刻度都绘制在了我们希望他们在的地方

接下来绘制指针:

绘制指针用的是 canvas.drawRoundRect 方法 ,需要指定指针的RectF属性,为了简化计算,我们仍然采用的是在Y轴上绘制然后旋转指定角度的方法.

首先获取当前的实践,计算时分秒各要旋转的角度值


获取指针 RectF 的示意图:

看懂了就简单多了,在Y轴绘制 RoundRect,然后旋转对应的角度即可,时分秒针旋转的角度不同,所以都需要用 canvas.save()  canvas.restore() 方法包括.直接上全部指针的代码:


最后在 onDraw() 内调用各绘制方法即可.然后每隔一秒钟刷新一次.最终的 onDraw 如下:


这里就差不多完成了。

这个自定义View是写在我个人的Demo合集当中的,就不单独提取出来浪费时间了.给出我的Demo合集地址:

LibManager

https://github.com/Brioal/LibManager

java文件就在 UILib module下的 watchboard package下.晚些时候会写个Demo合集的介绍.先放个动态图(只演示部分).

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值