- 今天在复习自定义View的相关知识,正好以画钟表来巩固一下知识。首先还是复习一下自定义View最主要的三个方法:
onMeasure() 计算本控件的宽高,如果继承自已有控件,则一般不需要重写此方法 ,具体分为View的onMeasure的测量方法和Viewgroup的onMeasure方法,如果是Viewgroup的onMeasure(),除了完成自己的测量之外还要测量子元素的大小。
(身为容器好辛苦哦~)
onLayout() 用于布局控件,对于不是继承ViewGroup的控件,一般不需要重写此方法
onDraw() 在绘制控件时候调用,把你脑海里想绘制的逻辑写在这里哦
为了帮助更好的记忆稍微复习一下Android的坐标系啦 (图画的有点丑哈哈哈)
好了现在开始我们的绘制吧首先看一看你们手上戴的表,仔细观察一下,如果没有表可以闭上眼睛想象一下,一个圆,上面有三个针,时针,分针,和秒针,时针最短,分针第二短,秒针最长......我去有点困了对不起大家
效果图:
言归正传首先需要我们创建一个自己的Clock_View集成自View重写他的构造方法,我们常用的构造方法主要有三种
public Clock_view(Context context) {
super(context);
}
public Clock_view(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
getTime();//获取系统当前时间
handler.sendEmptyMessageDelayed(1,1000);
}
public Clock_view(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
第一个构造函数:当不需要使用xml声明或者不需要使用inflate动态加载时候,实现此构造函数即可。
第二个构造函数: 当需要在xml中声明此控件,则需要实现此构造函数。并且在构造函数中把自定义的属性与控件的数据成员连接起来。
第三个构造函数:接受一个style资源。
所以目前我们用到的其实就只有第二个构造方法啦
然后我们要做一下全局变量的初始化:声明我们的画笔,同时还有接收时分秒的变量
private Paint paint;
private int hours,minute,seconds;//时,分,秒
然后重写我们最重要的onDraw方法
//设置画笔
paint = new Paint();
paint.setColor(Color.BLACK);//为画笔初始化颜色
//为画笔设置抗锯齿之所以要加这个是因为每一个像素都是一个小方块,但是我们需要让他绘制圆。官方注释:
* AntiAliasing smooths out the edges of what is being drawn, but is has
* no impact on the interior of the shape.
paint.setAntiAlias(true);
//设置画笔绘制文字的大小
paint.setTextSize(30);
//设置画笔的宽度
paint.setStrokeWidth(5);
好啦接下来就是画表的第一步啦 画一个圆心drawCircle,参数分别为X轴、Y轴、半径,画笔。这里我们取View中心点
//绘制 钟表圆心
canvas.drawCircle(getWidth() / 2, getHeight() / 2, 5, paint);
绘制表盘 这里一个比较重要的属性是setStyle(),里面要一个style的参数常用的有三种分别是填充,填充且描边,只描边。
//绘制表盘
paint.setColor(getResources().getColor(R.color.clock));
paint.setStyle(Paint.Style.FILL_AND_STROKE);//因为我们主要绘制的是表盘根据个人喜好绘制成一个填充且描边的style
canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2 - 10, paint);//和圆心同理改变半径以中心画圆
接下来开始绘制刻度了,首先我们先分析一下刻度时针转一圈要走十二个刻度所以我们要绘制12个点,然后每绘制一个点要让画布旋转rotate的第一个参数是旋转的量,单位是度。每次绘制完之后要恢复画布的原始状态
for (int i = 0; i < 12; i++) {
//保存画布的状态
canvas.save();
//旋转画布---- 旋转的角度,中心点X,中心点Y
canvas.rotate(360 / 12 * (i + 1), getWidth() / 2, getHeight() / 2);
//绘制刻度
canvas.drawLine(getWidth() / 2, getHeight() / 2 - getWidth() / 2 + 30, getWidth() / 2, (getHeight() - getWidth()) / 2 + 50, paint);
//绘制数字
canvas.drawText((i + 1) + "", getWidth() / 2 - 10, getHeight() / 2 - getWidth() / 2 + 80, paint);
//恢复画布的状态
canvas.restore();
//绘制
}
绘制完成之后画布上就有了1-12的数字,接下来我们开始绘制分钟 一小时有六十分钟所以绘制60个线。
//绘制分钟的刻度 -- 6度绘制一个线
for (int i = 0; i < 60; i++) {
//保存画布的状态
canvas.save();
//旋转画布 ---旋转的角度,中心点X,中心点Y
canvas.rotate(360 / 60 * (i + 1), getWidth() / 2, getHeight() / 2);
//绘制刻度
canvas.drawLine(getWidth() / 2, getHeight() / 2 - getWidth() / 2 + 10,
getWidth() / 2, getHeight() / 2 - getWidth() / 2 + 30, paint);
//绘制数字
//paint.setTextSize(20);
//canvas.drawText((i+1)+"",getWidth()/2-60,getHeight()/2-getWidth()/2+80,paint);
//恢复画布的状态
canvas.restore();
}
好,到现在为止刻度就已经绘制完成了,接下来就差我们的指针了
//绘制时针
canvas.save();
canvas.rotate(360 / 12 * hours + minute * 0.5f, getWidth() / 2, getHeight() / 2);
paint.setStrokeWidth(8);
canvas.drawLine(getWidth() / 2, getHeight() / 2,
getWidth() / 2, getHeight() / 2 - getHeight() / 10, paint);
canvas.restore();
//绘制分针
canvas.save();
canvas.rotate(360 / 60 * minute, getWidth() / 2, getHeight() / 2);
paint.setStrokeWidth(4);
canvas.drawLine(getWidth() / 2, getHeight() / 2,
getWidth() / 2, getHeight() / 2 - getWidth() / 3, paint);
canvas.restore();
//绘制秒针
canvas.save();
Log.d("celhgfkjreg", "handleMessage: " + seconds);
canvas.rotate(360 / 60 * seconds, getWidth() / 2, getHeight() / 2);
paint.setStrokeWidth(3);
canvas.drawLine(getWidth() / 2, getHeight() / 2,
getWidth() / 2, getHeight() / 2 - getHeight() / 4, paint);
canvas.restore();
invalidate();
这里重点说一下invalidate()这个方法 从字面意义上来说invalidate是无效的意思,但是可不能就这样就去猜测他的实际作用,invalidate的作用是刷新我们的View 且他是在主线程调用 invalidate的底层源码并没有走
measure和layout方法
也就是并没有重新测量位置和大小 所以在这里使用invalidata是最合适不过的啦。如果想看invalidate的具体源码有兴趣的话可以参考:
https://blog.youkuaiyun.com/xw13782513621/article/details/73863006
好,现在准备工作已经完成了,现在通过java的日历类来获取到当前的时间 对应的时 分秒
public void getTime() {
Calendar calendar = Calendar.getInstance();//日历类
hours = calendar.get(Calendar.HOUR);
minute = calendar.get(Calendar.MINUTE);
seconds = calendar.get(Calendar.SECOND);
}
到这里呢我们就绘制完了一个自己的时钟,哎,细心的同学会发现表怎么不会动呢?哈哈哈,还有最重要的一步喽就是通过handler来让我们的表动起来啦。
@SuppressLint("HandlerLeak")
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//得到系统时间
getTime();
//发送请求 延时1秒
handler.sendEmptyMessageDelayed(1, 1000);
}
};
好啦大功告成~~~