前言:
这是自定义view的的第二篇文章,自定义view是android工程师进阶必备的技能点,所以要想往中高级工程师发展,必须要学会之定义view,好多人说自定义比较难,其实不然,自定义view只不过比较繁琐,需要注意的点比较多,比如:测量、布局、绘制,就绘制来说就够你看一周也看不完,当然还有和view的交互等,所以一定要静下心来,不急不躁,循序渐进的去学,下面我们来绘制一个表玩玩。
先看下效果图(下面我们将分步来绘制,便于你的理解):
初始化:
注释已经很清楚了,我主要说下这个方法:
protected void onSizeChanged(int w, int h, int oldw, int oldh)
这个方法在视图大小发生改变时调用。所以我们在这里获取宽高,只需要关注前两个参数。
绘制外部圆:
接下来的代码全是在onDraw()方法中执行:
相信了解Canva和Paint的童鞋一定对画圆API很熟悉,四个参数分别是:
圆心X坐标:mWidth / 2
圆心Y坐标: mHeight / 2
圆半径:mRadius
画笔对象: mCirclePaint
这个想必也不用解释了~
绘制刻度以及刻度值:
由于里面加的注释太多,可能图片有点不清晰,建议放大查看
表里面有粗体刻度和小刻度,粗体刻度就是整点时刻,这个比较容易理解。那我们的表盘刻度值一共有60个,所以我决定循环画出,当然了,循环体会区别粗体和小刻度:
粗体刻度:表盘中分别有12,1,2……11 只要是被5整除的就是粗体刻度。说白了就是画一条线段,注意看这个方法:
//如图1:A点坐标(mWidth / 2, mHeight / 2 - mWidth / 2) B点(mWidth / 2, mHeight / 2 - mWidth / 2 + 60, mDegreePaint)
canvas.drawLine(mWidth / 2, mHeight / 2 - mWidth / 2, mWidth / 2, mHeight / 2 - mWidth / 2 + 60, mDegreePaint);
为了促进我们的理解,我们结合图来看:
android坐标系不同于数学坐标系,android中以内容布局的左上角为坐标原点,水平向右为X轴正方向,垂直向下为Y轴正方向,如图我们的A点坐标(mWidth / 2, mHeight / 2 - mWidth / 2),B点(mWidth / 2, mHeight / 2 - mWidth / 2 + 60, mDegreePaint)其实就是两点决定一条直线,好了这是粗体刻度,那小刻度也一样道理,只不过短了一点,已经很详细了~
绘制刻度值:
String num = String.valueOf(i / 5);
if ("0".equals(num)) {
num = "12";
}
//绘制时间数值,X坐标:半径-数值文本宽度一半 Y坐标:90
canvas.drawText(num, mWidth / 2 - mDegreePaint.measureText(num) / 2, mHeight / 2 - mWidth / 2 + 90, mDegreePaint);
API我就不说了,一看就懂,我们绘制文本肯定是从文本左边底部开始时绘制,也就是坐下方位,什么还不明白?
简单说下坐标:
O是圆心
M点是 “12” 文本的X坐标中心点(你就把12看做是一个矩形框)M点就是矩形框底边的中间点
L就是我刚才说的左下坐标,我们绘制文本开始的地方,处于12的底部且左边
H是以L点为起始点,连接LH平行于MO
这下就比较好讲了:我们的目的就是求出L点坐标?
圆的半径=mWidth/2
点L的X坐标=mWidth/2-LM
LM是什么?12的宽度的一半,android已经有API可以求出文本宽度了:mDegreePaint.measureText(num)
点L的Y坐标就是距离X轴的距离,这个就是比较随意了。比如我们设置90
所以点L( mWidth / 2 - mDegreePaint.measureText(num) / 2, mHeight / 2 - mWidth / 2 + 90 )
是不是很简单?
可能有人会骂街!你这个是首个垂直刻度,其他倾斜的刻度呢!吓得我菊花一紧:
看码,哦不!看代码:
//画布旋转操作:一圈360度,共有60个刻度格,每个6度,所以每次旋转6度,也就是下面方法的第一个参数,后面两个参数其实就是旋转中心
canvas.rotate(6, mWidth / 2, mHeight / 2);
Canvas提供 位移、缩放、 旋转、错切,上面我们就是利用了旋转姿势~
这样我们只需要在for循环调用这个方法,刻度就会一一绘制出来。是不是很happy~
绘制指针:
绘制时针:
赶快拿出你去年买的表!接下来用得着!
canvas.rotate((hour * 5*6)+(minute*0.5f),mWidth/2,mHeight/2);
canvas.drawLine(mWidth/2,mHeight/2, mWidth / 2, mHeight / 2 - mWidth / 2 + 250, mHourPaint);
我们知道时针转走过一个小时,扫描的角度是:30度=5*6; 也就是5个刻度格 x每个刻度度数;
但这都是整时刻扫过的角度,我们要不是整时刻呢?比如5:08;所以我们还要加上分钟扫过的角度;
试想:分钟转一圈,我们的时针扫描30度,也就是说 0.5度/min (每分钟时针扫描0.5度)
好了,这就是时针计算过程!
那么还有两个API说明下:
canvas.save() :就是我们在做新的操作之前,先把以前画过的东西保存起来,也就是保存状态
canvas.restore():就是恢复save()之前的操作,保留现在的操作,我觉得就是合并的意思(个人理解)
分针和秒针
//分针
canvas.save();
canvas.rotate(minute * 6,mWidth/2,mHeight/2);
canvas.drawLine(mWidth/2,mHeight/2, mWidth / 2, mHeight / 2 - mWidth / 2 + 150, mMinutePaint);
canvas.restore();
//秒针
canvas.save();
canvas.rotate(second * 6,mWidth/2,mHeight/2);
canvas.drawLine(mWidth/2,mHeight/2, mWidth / 2, mHeight / 2 - mWidth / 2 + 100, mSecondPaint);
canvas.restore();
这里就不做解释了比较简单~
我们的秒针必须要每秒刷新一次,才能模拟钟表,所以调用 postInvalidateDelayed(1000);重新绘制onDraw()
方法说明:
我们上面主要是运用了画布(Canvas)的旋转,当然画布还有其他操作:
如:
位移(translate)
缩放(scale)
错切(skew)
这些都是画布的基本操作,有兴趣的可以自行google,毕竟网上教程很多,我就不多说了。
案例推荐:
github地址(如果喜欢,请亲~给个start 谢谢~)