自定义View一直属于开发当中比较难得问题,对于计算,逻辑各方面都要进行考虑。在前面的博客中,我曾经将自定义view涉及到的几个函数的作用通过案例进行了介绍。在此篇博客中,将使用这些这是点完成钟表view的编写。效果图如下:
观察发现,很明显图中的两个钟表视图在Android原生控件中并没有被定义。所以通过自定义控件来实现。
涉及到的知识点:
1.因为在原生控件中并不包含相关控件,所以使用完全自定义View的方式,继承View类
2.绘制控件涉及到画圆形,直线的方法,但是表盘刻度有12个,为了方便绘制,可以旋转画布
3.上下两个钟表颜色不一样,可创建自定义属性进行改变
4.时钟视图要随着时间的变化,改变时针分针秒针的位置,需要每秒钟重新绘制一下界面
那首先在values文件夹当中创建attrs.xml文件,编写改变时钟的自定义属性。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ClockView">
<attr name="clockColor" format="color"/>
</declare-styleable>
</resources>
然后编写ClockView.java文件,在java代码中编写钟表视图。
package com.animee.day03.custom;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import com.animee.day03.R;
import java.util.Calendar;
public class ClockView extends View{
Paint paint;
int hours = 0; //时
int minute = 0; //分
int seconds = 0; //秒
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
// 接受信息
if (msg.what == 1) {
// 重新获取时间
getTime();
invalidate(); //重新绘制界面
handler.sendEmptyMessageDelayed(1,1000);
}
}
};
/*初始化画笔*/
public void initPaint(){
paint = new Paint();
paint.setAntiAlias(true);
}
public ClockView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
// 初始化画笔
initPaint();
getTime();
// 获得画笔在布局当中设定的颜色
// 1.声明属性attrs里面 2.布局引用声明好的属性 3.代码当中获取布局引用的属性
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ClockView);
int color = typedArray.getColor(R.styleable.ClockView_clockColor, Color.BLACK);
paint.setColor(color);
// 回收属性
typedArray.recycle();
}
@Override
protected void onDraw(Canvas canvas) {
// 实现绘制界面操作
super.onDraw(canvas);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(8);
// 设置内边距
setPadding(20,20,20,20);
// 绘制外层的大圆
canvas.drawCircle(getWidth()/2,getHeight()/2,getWidth()/2-20,paint);
// 设置画笔的粗细
paint.setStrokeWidth(4);
// 绘制内圈圆
canvas.drawCircle(getWidth()/2,getHeight()/2,getWidth()/2-30,paint);
// 绘制轴心
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(getWidth()/2,getHeight()/2,10,paint);
// 绘制表的刻度 12个,直线,但是起始点和结尾点本身不好好,所以通过旋转画笔实现
for (int i = 1; i <=12 ; i++) {
// 保存画笔原有内容
canvas.save();
// 旋转指定角度
canvas.rotate(360/12*i,getWidth()/2,getHeight()/2);
canvas.drawLine(getWidth()/2,40,getWidth()/2,50,paint);
// 恢复旋转前的状态
canvas.restore();
}
// 绘制时针,分支和秒针
// 2点34分
paint.setStrokeWidth(8);
canvas.save();
canvas.rotate(30*hours+0.5f*minute,getWidth()/2,getHeight()/2);
canvas.drawLine(getWidth()/2,getHeight()/2,getWidth()/2,getHeight()/2-getHeight()/5,paint);
canvas.restore();
// 绘制分针
paint.setStrokeWidth(5);
canvas.save();
canvas.rotate(6*minute,getWidth()/2,getHeight()/2);
canvas.drawLine(getWidth()/2,getHeight()/2,getWidth()/2,getHeight()/2-getHeight()/4,paint);
canvas.restore();
// 绘制秒针
paint.setStrokeWidth(3);
canvas.save();
canvas.rotate(6*seconds,getWidth()/2,getHeight()/2);
canvas.drawLine(getWidth()/2,getHeight()/2,getWidth()/2,getHeight()/2-getHeight()/3,paint);
canvas.restore();
// 提醒1秒针之后,刷新界面
handler.sendEmptyMessageDelayed(1,1000);
}
/*获取当前时间的方法*/
public void getTime(){
Calendar calendar = Calendar.getInstance();
hours = calendar.get(Calendar.HOUR);
minute = calendar.get(Calendar.MINUTE);
seconds = calendar.get(Calendar.SECOND);
}
// 因为整体依然是个圆形,所以要保证宽高相同
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 1.获取宽高模式
int wmode = MeasureSpec.getMode(widthMeasureSpec);
int hmode = MeasureSpec.getMode(heightMeasureSpec);
// 2.获取宽高最大尺寸
int wsize = MeasureSpec.getSize(widthMeasureSpec);
int hsize = MeasureSpec.getSize(heightMeasureSpec);
// 3.判断模式,获取最终显示尺寸
int size = 400;
if (wmode == MeasureSpec.EXACTLY){
if (hmode == MeasureSpec.EXACTLY) {
// 宽高都为具体值,谁小就是谁
size = Math.min(wsize,hsize);
}else{
// 宽为具体值,高为wrap_content
size = wsize;
}
}else{
// 宽为wrap_content
if (hmode== MeasureSpec.EXACTLY) {
// 高度为具体值
size = hsize;
}else{
// 宽高都为warp_content
size = 400;
}
}
// 将测量好的值设置给控件宽高
setMeasuredDimension(size,size);
}
}
然后就可以在布局文件中直接引用这个自定义的ClockView文件了,注意引入时要使用 自定义的包名.类名
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp"
android:gravity="center_horizontal">
<com.animee.day03.custom.ClockView
android:layout_width="300dp"
android:layout_height="300dp"
/>
<com.animee.day03.custom.ClockView
android:layout_width="200dp"
android:layout_height="200dp"
app:clockColor="#FF0000"
/>
</LinearLayout>
按照以上内容的顺序编写,就能够完成这个时钟控件了。有兴趣的同学可以编写一下,尝试运行!
感谢您的阅读~