自定义View一直属于开发当中比较难得问题,对于计算,逻辑各方面都要进行考虑。在前面的博客中,我曾经将自定义view涉及到的几个函数的作用通过案例进行了介绍。在此篇博客中,将使用这些这是点完成钟表view的编写。效果图如下:

Android 自定义 时间线用View还是ViewGroup android自定义时钟_xml

观察发现,很明显图中的两个钟表视图在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>

按照以上内容的顺序编写,就能够完成这个时钟控件了。有兴趣的同学可以编写一下,尝试运行!

感谢您的阅读~