【Android Canvas系列】使用canvas绘制简单的clock

最近在看HTML5 canvas绘制方面的书籍,感觉和Anroid Canvas方面的知识有比较相通的地方。忽然萌生一个用Android Canvas实现书中例子程序的想法,一来巩固自己Android Canvas方面的知识,二来帮助一些想了解Android Canvas方面知识的新手们。

1. 目标

html5实现的clock截图
上图是使用HTML5 Canvas绘制的时钟,本次的目标就是使用Android Canvas实现一个一模一样的时钟。

2.API 列表
  • drawCircle //用于绘制钟表外框和中心的小圆点
  • drawText //用于绘制钟表外框上的文字
  • drawLine //用于绘制钟表的表针
3.代码分析
/**
 * Created by sogoe on 2015/3/14.
 */
public class ClockView extends View {
···
    @override
    protected void onDraw(Canvas canvas) {
        if(!isInited)
            initClock();

        drawCircle(canvas);
        drawCenter(canvas);
        drawNumeral(canvas);
        drawHands(canvas);

        this.postInvalidateDelayed(1000);
    }
}

layout在计算完布局内每个view的长宽后,会调用view的draw函数。ondraw函数则会在draw函数中被调用,用于绘制内容。那么,关于钟表所有的绘制工作将会在ondraw函数内实现。initClock会执行相关参数的初始化工作,drawCircle是绘制钟表外圈园的函数,drawCenter是绘制钟表中心点的函数,drawNumeral是绘制钟表数字的函数,drawHands是绘制钟表表针的函数。注意,最后的postInvalidateDelayed函数,其内部实现也是使用handler.sendMessageDelayed,然后再handleMessage里执行invalidate函数。invalidate函数会请求重置整个view。

void initClock() {
    height = this.getHeight();
    width = this.getWidth();
    padding = 35;
    fontSize = 10;
    int min = Math.min(this.getHeight(), this.getWidth());
    radius = min/2 - padding;
    handTruncation = min/25;
    hourHandTruncation = min/10;
    numeralRadius = radius + numeralSpacing;

    paint = new Paint();
    isInited = true;
}

initClock函数就是对钟表的参数进行初始化。或许你会想,初始化为什么不放到构造函数里。仔细看,在代码第一二行里使用了getHeight和getWidth函数,如果放到构造函数里,获取到的长宽为0。因为,在view初始化的时候,系统也并不知道这个view的具体长宽,其父layout会不断调用view的onMeasure函数来计算这个view具体的长宽,所以,执行在ondraw之前,view的长宽才最终被确定下来。因此,在ondraw里执行长宽的初始化最保险。

void drawCircle(Canvas canvas) {
    paint.reset();                                                                        
    paint.setColor(getResources().getColor(android.R.color.black));
    paint.setStrokeWidth(1);
    paint.setStyle(Paint.Style.STROKE);
    paint.setAntiAlias(true);
    canvas.drawCircle(width/2, height/2, radius, paint);
}

drawCircle就是绘制一个圆圈,这里使用的paint设置style为stroke,表示仅绘制轮廓,不对其内部进行填充。下面的drawCenter函数则要将其设置为fill,因为要绘制一个实心的圆,所以要对其内部填充。

void drawCenter(Canvas canvas) {
    paint.setStyle(Paint.Style.FILL);
    canvas.drawCircle(width/2, height/2, 5, paint);
}

将paint的style设置为fill,绘制实心圆。其他的设置沿用上面函数设置。

void drawNumeral(Canvas canvas) {
     paint.setTextSize(fontSize);
     float fontWidth;
     double angle;
     int x, y;
     for (int number : numbers) {
        fontWidth = paint.measureText(String.valueOf(number));
        angle = Math.PI / 6 * (number - 3);
        x = (int) (width / 2 + Math.cos(angle) * numeralRadius - fontWidth / 2);
        y = (int) (height / 2 + Math.sin(angle) * numeralRadius + fontSize / 3);
        canvas.drawText(String.valueOf(number), x, y, paint);
     }
}

首先设置字体的大小。留意paint.measureText函数,它返回按照paint当前的配置计算文本在屏幕上所占的长度,在canvas的文本绘制中相当有用。然后就是依次绘制钟表上的数字。x,y坐标点的计算可以在草稿纸上画一下,比较容易理解。

void drawHand(Canvas canvas, double loc, boolean isHour) {
     double angle = Math.PI * loc / 30 - Math.PI / 2;
     int handRadius = isHour ? radius - handTruncation - hourHandTruncation : radius - handTruncation;

     canvas.drawLine(width / 2, height / 2,
                (float) (width / 2 + Math.cos(angle) * handRadius),
                (float) (height / 2 + Math.sin(angle) * handRadius),
                paint);
 }

 void drawHands(Canvas canvas) {
      Calendar calendar = Calendar.getInstance();
      float hour = calendar.get(Calendar.HOUR_OF_DAY);
      hour = hour > 12 ? hour - 12 : hour;

      drawHand(canvas, (hour + calendar.get(Calendar.MINUTE) / 60.f) * 5.f, true);
      drawHand(canvas, calendar.get(Calendar.MINUTE), false);
      drawHand(canvas, calendar.get(Calendar.SECOND), false);
}

绘制钟表表针的两个函数。需要留意的是drawLine函数,绘制从一个点至另一个点的直线。drawHands函数里,就是获取当前的时间,然后绘制针表,具体起始点和终点x,y的计算,在草稿纸上写写划划,很容易理解。

4.运行结果

Android运行结果

5.代码地址

GitHub代码地址

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值