当前位置: 移动互联网学院 > Android开发 > 仿小米时钟-简约版
仿小米时钟-简约版 时间:2017-07-27     来源:移动互联网学院

小米的UI相对来说是做的很美观的,这里就简单的模仿一下小米的时钟,上面部分效果没有加上,先看下效果图:

下面是小米的UI:

去实现这个效果其实只需要分为三个步骤就可以了: 

1、画外围的圆弧、数字 

2、画刻度线 

3、画时针、分针、秒针

整个View的一些属性,如颜色、背景等等可以使用自定义属性进行设置增加其灵活性,这里为了简单就直接在代码中设置了。这里只是大致的思路,没有贴出全部代码。基本的工作就不做多的介绍,这里创建一个类ClockView继承View,初始化各个Paint、设置属性、各个坐标的变量等等。

1、画外围的圆弧、数字

首先定义一个RectF确定圆弧的区域:

// 设置圆弧绘制区域

float left = (float) (clockX - 1.2 * radius);// 根据原点的中心左边和刻度线的半径

float top = (float) (clockY - 1.2 * radius);

float right = (float) (clockX + 1.2 * radius);

float bottom = (float) (clockY + 1.2 * radius);

RectF rect = new RectF(left,top,right,bottom);

这里的难点是文字的绘制,文字其实也是一块矩形区域,它的锚点在左下角,所以绘制文字时为了在弧线的中间要注意锚点的坐标,以右边的“3”为例,绿线是圆弧区域的右边界,它的X坐标是已经定义好的–right,那现在只需要知道“3”文字的宽度textWidth,拿right减去textWidth/2正好就是锚点的X坐标。在水平方向也是居中的,那只需要用圆心的y坐标clockY加上文字高度的一半,文字高度是自己设定的,参考下面的示意图,剩下的“6”、“9”、“12”以此类推: 

private void drawArcLines(Canvas canvas) {

        // 设置圆弧绘制区域

        float left = (float) (clockX - 1.2 * radius);// 根据原点的中心左边和刻度线的半径

        float top = (float) (clockY - 1.2 * radius);

        float right = (float) (clockX + 1.2 * radius);

        float bottom = (float) (clockY + 1.2 * radius);

        RectF rect = new RectF(left,top,right,bottom);

        // 圆弧设置

        arcPaint.setStyle(Paint.Style.STROKE);

        arcPaint.setStrokeWidth(1);

        arcPaint.setARGB(125, 255, 255, 255);

        // 字体设置

        textPaint.setTextSize(40);

        textPaint.setARGB(125, 255, 255, 255);

        String num = "0";

        // 文字的坐标

        float textX = 0;

        float textY = 0;

        float textWidth = 0;

        // 通过循环,每90度绘制以此圆弧和文字

        for (int i = 0; i < 360; i += 90) {

            // 画圆弧,注意圆弧的起始终止弧度,留一定的间隙给文字

            canvas.drawArc(rect, 5 + i, 80, false, arcPaint);

            // 计算文字锚点坐标

            switch (i) {

                case 0:

                    num = "3";

                    textWidth = textPaint.measureText(num);

                    textX = right - textWidth / 2;

                    textY = clockY + 10;

                    break;

                case 90:

                    num = "6";

                    textWidth = textPaint.measureText(num);

                    textX = clockX - textWidth / 2;

                    textY = bottom + 10;

                    break;

                case 180:

                    num = "9";

                    textWidth = textPaint.measureText(num);

                    textX = left - textWidth / 2;

                    textY = clockY + 10;

                    break;

                case 270:

                    num = "12";

                    textWidth = textPaint.measureText(num);

                    textX = clockX - textWidth / 2;

                    textY = top + 10;

                    break;

            }

            // 绘制文字

            canvas.drawText(num, textX, textY, textPaint);

        }

    }

2、画刻度线

画刻度线相对来说比较简单,知道刻度线的其实终止点坐标,例如下面的示意图,起始点a的想x、y坐标可以利用绿色圆形的半径乘以对应的三角函数,绿色圆形的半径radius可以直接根据屏幕的大小设定,这里不做解释了,终止点的坐标那就是粉色圆形的半径乘以三角函数,粉色圆形半径则可以设置成radius的百分之多少。计算的结果必须加上相应圆形的x或y坐标,这样才是真正相对于界面的坐标值。 

知道怎么画一条之后,这一圈的刻度线就可以利用循环绘制,画多少条则根据每一条间隔的角度可以设定。

// 画短线

    private void drawShortLines(Canvas canvas) {

        paint.setStrokeWidth(4);

        for (double i = 0; i < 2 * PI; i += PI / 60) {

            float startX = (float) (radius * Math.cos(i)) + clockX;

            float startY = (float) (radius * Math.sin(i)) + clockY;

            float endX = (float) (0.85 * radius * Math.cos(i)) + clockX;

            float endY = (float) (0.85 * radius * Math.sin(i)) + clockY;

            paint.setARGB(125, 255, 255, 255);

            canvas.drawLine(startX, startY, endX, endY, paint);

        }

    }

3、画时针、分针、秒针

画这几个指针的思路是一样的,通过路径Path进行绘制 

1、秒针 

用 Path 绘制一个指向 12点钟 的三角形,通过不断旋转画布实现秒针的旋转,三个点的坐标需要自己去调整:

private void drawSecPath(Canvas canvas){

        secPaint.setARGB(255,255,255,255);

        canvas.save();

        canvas.rotate(secDregee,clockX,clockY);// 旋转该层画布

        float offset =  radius * 0.15f;

        secPath.moveTo(clockX, clockY - 0.8f * radius);

        secPath.lineTo(clockX + offset / 2,clockY - 0.8f * radius + 0.86f * offset);

        secPath.lineTo(clockX - offset / 2,clockY - 0.8f * radius + 0.86f * offset);

        secPath.close();

        canvas.drawPath(secPath,secPaint);

        canvas.restore();

    }

2、分针 

分针可以看做是一个梯形,同样用路径Path绘制:

private void drawMinHand(Canvas canvas){

        minPaint.setARGB(200,255,255,255);

        canvas.save();

        canvas.rotate(minDregee,clockX,clockY);

        minPath.moveTo(clockX - 0.02f * radius,clockY - 0.1f * radius);

        minPath.lineTo(clockX - 0.01f * radius,clockY - 0.7f * radius);

        minPath.lineTo(clockX + 0.01f * radius,clockY - 0.7f * radius);

        minPath.lineTo(clockX + 0.02f * radius,clockY - 0.1f * radius);

        minPath.close();

        canvas.drawPath(minPath,minPaint);

        canvas.restore();

 }

2、时针 

时针也可以看做是一个梯形,同样用路径Path绘制:

private void drawHourHand(Canvas canvas){

        hourPaint.setARGB(255,255,255,255);

        canvas.save();

        canvas.rotate(hourDregee,clockX,clockY);

        hourPath.moveTo(clockX - 0.03f * radius,clockY - 0.06f * radius);

        hourPath.lineTo(clockX - 0.02f * radius,clockY - 0.6f * radius);

        hourPath.lineTo(clockX + 0.02f * radius,clockY - 0.6f * radius);

        hourPath.lineTo(clockX + 0.03f * radius,clockY - 0.06f * radius);

        hourPath.close();

        canvas.drawPath(hourPath,hourPaint);

}

在中间还有一个圆环,直接画一个空心圆并设置一些线条的宽度即可:

private void drawCoverCircel(Canvas canvas) {

        circlePaint.setColor(Color.WHITE);

        circlePaint.setStyle(Paint.Style.STROKE);

        circlePaint.setStrokeWidth(0.06f * radius);

        canvas.drawArc(clockX - 0.08f * radius,clockY - 0.08f * radius,

                clockX + 0.08f * radius,clockY + 0.08f * radius,

                0f,360f,false,circlePaint);

}

终效果: 

到这里所有的图形已绘制完毕,现在则需要让时分秒针动起来,在这三个绘制的方法中都有一个旋转的画布的方法,其中第一个参数就是指定画布旋转的角度,只要不断的改变其参数值就可让时分秒针动起来,这里在定义一个方法,将系统时间换算成时分秒针对应的旋转角度:

private void getTime(){

        Calendar calendar = Calendar.getInstance();

        float milliSecond = calendar.get(Calendar.MILLISECOND);

        // 在时分秒后面加上分、秒、毫秒,可以让指针旋转更加平滑,减少顿挫感

        float second = calendar.get(Calendar.SECOND) + milliSecond / 1000;

        float min = calendar.get(Calendar.MINUTE) + second / 60;

        float hour = calendar.get(Calendar.HOUR) + min / 60;

        // 角度换算

        secDregee = second / 60 * 360 ;

        minDregee = min / 60 * 360;

        hourDregee = hour / 12 * 360;

}

终的onDraw方法如下:

protected void onDraw(final Canvas canvas) {

        super.onDraw(canvas);

        canvas.drawARGB(255, 40, 120, 200);

        getTime();

        drawArcLines(canvas);

        drawShortLines(canvas);

        drawSecPath(canvas);

        drawMinHand(canvas);

        drawHourHand(canvas);

        drawCoverCircel(canvas);

        // 在主线程调用界面刷新,刷新时调用onDraw方法,循环往复

        invalidate();

}

好了,到这里整个流程已经结束,现在这个ClockView就可以在布局文件中直接使用了:

<LinearLayout xmlns:android="//schemas.android.com/apk/res/android"

    xmlns:tools="//schemas.android.com/tools"

    android:id="@+id/activity_main"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:background="#123"

    android:orientation="vertical"

    tools:context="com.hqyj.clockview.MainActivity">

    <com.hqyj.clockview.ClockView

        android:layout_gravity="center_horizontal"

        android:layout_width="match_parent"

        android:layout_height="match_parent"

        android:id="@+id/clockView" />

</LinearLayout>