Android 画一个闹钟

结果图

先上结果图(下面的代码是没有数字左下角的点的,这个点就是为了提醒文字的(x,y)是它的左下角)

画闹钟其实就是自定义一个View,重写它的onDraw()

我创建了

复制代码
public class ClockView extends View 

重写onDraw()方法如下:

须知:画布的(0,0)在左上角。

画外面的大圆圈就不说了,这个圈可画可不画。

其中x,y是圆心

java 复制代码
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        /* begin: 画外面大圆圈 */
        Paint paintCircle = new Paint();
        paintCircle.setColor(Color.BLACK);
        paintCircle.setStrokeWidth(5);
        paintCircle.setStyle(Paint.Style.STROKE);
        int x = wm.getDefaultDisplay().getWidth() / 2;  //x轴中点
        int y = wm.getDefaultDisplay().getHeight() / 2;  //y轴中点
        int r = Math.min(x, y) - 10;
        canvas.drawCircle(x, y, r, paintCircle);
        /*end*/
    }

画刻度

须知:

(1)闹钟的刻度有60条;(2)每5条就是小时刻度(加粗加长);(3)每一条隔着360/60度。

思路:

每次画刻度都将cancas旋转360/60度。逢%5==0画小时刻度线

java 复制代码
        /*begin 画刻度 60条*/
        Paint paint = new Paint();
        for (int i = 0; i < 60; i++) {
            if (i % 5 == 0) {
                paint.setStrokeWidth(8);
                canvas.drawLine(x, y - r, x, y - r + 40, paint);  //小时刻度线长度为40
            } else {
                paint.setStrokeWidth(4);
                canvas.drawLine(x, y - r, x, y - r + 20, paint);  //刻度线长度为20
            }
            canvas.rotate(6, x, y);  //以(x,y)为中心旋转6度
        }
        /*end*/

画数字

须知:

(1)要考虑到画数字的(x,y)并不是其中心点,而是左下角,画的时候需要调整数字位置为**(x-** 文字宽度/2,y+文字高度/2)

使用getTextBounds获取文字矩阵

java 复制代码
   //检索文本边界框并存储到边界。在边界(由调用者分配)中返回包含所有字符的最小矩形,隐含原点为 (0,0)。
    public void getTextBounds(String text, int start, int end, Rect bounds)

下面是画数字代码

java 复制代码
//画数字
        for (int i = 1; i <= 12; i++) {
            paint.setStrokeWidth(3);
            paint.setTextSize(40);
            double radius = Math.toRadians(30 * i); //角度转为弧度,不转是算不出正确结果的
            Rect rect = new Rect();
            paint.getTextBounds(i + "", 0, (i + "").length(), rect); 获取文字的bounds
            //注意:默认情况下文字的x,y是左下角
            canvas.drawText(i + "", (int) (x + Math.sin(radius) * (r - 70) - rect.width() / 2), (int) (y - Math.cos(radius) * (r - 70) + rect.height() / 2), paint);
        }

画小圆圈

下面是画小圆圈的,就是时钟分针秒针起点的圆心

java 复制代码
//画圆
        paint.setStrokeWidth(5);
        paint.setColor(Color.BLACK);
        paint.setStyle(Paint.Style.STROKE);
        canvas.drawCircle(x, y, 20, paint);
        drawHand(canvas, x, y, r);

画时分秒针

针终点图计算原理如下

运用以上公式再结合画刻度线得知的每一刻度都是360/60度,就可得知每一秒占用了6度,以此计算每根针应该在什么位置就好。

java 复制代码
    public void drawHand(Canvas canvas, int x, int y, int r) {
        /*begin 获取时间*/
        Calendar calendar = Calendar.getInstance();
        int second = calendar.get(Calendar.SECOND);
        int minute = calendar.get(Calendar.MINUTE);
        int hour = calendar.get(Calendar.HOUR);
        double angleSecond = Math.toRadians(second * 360 / 60);
        double angleMinute = Math.toRadians(minute * 360 / 60);
        double angleHour = Math.toRadians(hour * 360 / 12);
        Paint paint = new Paint();
        //画时针
        paint.setStrokeWidth(30);
        paint.setColor(0xff991339);
        paint.setStrokeCap(Paint.Cap.ROUND);
        //(r-200)就是时针的长度
        canvas.drawLine(x, y, (int) (x + Math.sin(angleHour) * (r - 200)), (int) (y - Math.cos(angleHour) * (r - 200)), paint);
        //画分针
        paint.setStrokeWidth(20);
        paint.setColor(Color.BLACK);
        paint.setStrokeCap(Paint.Cap.ROUND);
        //(r-150)就是分针的长度
        canvas.drawLine(x, y, (int) (x + Math.sin(angleMinute) * (r - 150)), (int) (y - Math.cos(angleMinute) * (r - 150)), paint);
        //画秒针
        paint.setStrokeWidth(10);
        paint.setColor(Color.BLUE);
        paint.setStrokeCap(Paint.Cap.ROUND);
        //(r-100)就是秒针的长度
        canvas.drawLine(x, y, (int) (x + Math.sin(angleSecond) * (r - 100)), (int) (y - Math.cos(angleSecond) * (r - 100)), paint);
    }

动起来

图画完就该让它动起来了

这里用的方法是handler,每秒发送一次message,然后handler调用invalidate()刷新重绘视图。

这里需要找个地方start线程。

java 复制代码
    private int TIME = 1000; //1s
    Thread t = new Thread() {
        @Override
        public void run() {
            try {
                while (true) {
                    h.sendEmptyMessage(0);
                    sleep(1000);
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    };
    Handler h = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            invalidate();   //刷新重绘
        }
    };

全代码

View

java 复制代码
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.util.Calendar;

public class ClockView extends View {
    private WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
    final String TAG = getClass().getSimpleName();
    private int TIME = 1000; //1s
    Thread t = new Thread() {
        @Override
        public void run() {
            try {
                while (true) {
                    h.sendEmptyMessage(0);
                    sleep(1000);
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    };
    Handler h = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            invalidate();   //刷新重绘
        }
    };

    public ClockView(Context context) {
        super(context);
        Log.d(TAG, "ClockView: 1");
    }

    public ClockView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        Log.d(TAG, "ClockView: 2");
        t.start();
    }

    public ClockView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        Log.d(TAG, "ClockView: 3");
    }

    public ClockView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        Log.d(TAG, "ClockView: 4");
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        /* begin: 画外面大圆圈 */
        Paint paintCircle = new Paint();
        paintCircle.setColor(Color.BLACK);
        paintCircle.setStrokeWidth(5);
        paintCircle.setStyle(Paint.Style.STROKE);
        int x = wm.getDefaultDisplay().getWidth() / 2;  //x轴中点
        int y = wm.getDefaultDisplay().getHeight() / 2;  //y轴中点
        int r = Math.min(x, y) - 10;
        canvas.drawCircle(x, y, r, paintCircle);
        /*end*/
        /*begin 画刻度 60条*/
        Paint paint = new Paint();
        for (int i = 0; i < 60; i++) {
            if (i % 5 == 0) {
                paint.setStrokeWidth(8);
                canvas.drawLine(x, y - r, x, y - r + 40, paint);
            } else {
                paint.setStrokeWidth(4);
                canvas.drawLine(x, y - r, x, y - r + 20, paint);
            }
            canvas.rotate(6, x, y);
        }
        /*end*/
        //画数字
        for (int i = 1; i <= 12; i++) {
            paint.setStrokeWidth(3);
            paint.setTextSize(40);
            double radius = Math.toRadians(30 * i); //角度转为弧度,不转是算不出正确结果的
            Rect rect = new Rect();
            paint.getTextBounds(i + "", 0, (i + "").length(), rect);
            //注意:默认情况下文字的x,y是左下角
            canvas.drawText(i + "", (int) (x + Math.sin(radius) * (r - 70) - rect.width() / 2), (int) (y - Math.cos(radius) * (r - 70) + rect.height() / 2), paint);
        }
        //画圆
        paint.setStrokeWidth(5);
        paint.setColor(Color.BLACK);
        paint.setStyle(Paint.Style.STROKE);
        canvas.drawCircle(x, y, 20, paint);
        drawHand(canvas, x, y, r);
    }


    public void drawHand(Canvas canvas, int x, int y, int r) {
        /*begin 获取时间*/
        Calendar calendar = Calendar.getInstance();
        int second = calendar.get(Calendar.SECOND);
        int minute = calendar.get(Calendar.MINUTE);
        int hour = calendar.get(Calendar.HOUR);
        double angleSecond = Math.toRadians(second * 360 / 60);
        double angleMinute = Math.toRadians(minute * 360 / 60);
        double angleHour = Math.toRadians(hour * 360 / 12);
        Paint paint = new Paint();
        //画时针
        paint.setStrokeWidth(30);
        paint.setColor(0xff991339);
        paint.setStrokeCap(Paint.Cap.ROUND);
        //(r-200)就是时针的长度
        canvas.drawLine(x, y, (int) (x + Math.sin(angleHour) * (r - 200)), (int) (y - Math.cos(angleHour) * (r - 200)), paint);
        //画分针
        paint.setStrokeWidth(20);
        paint.setColor(Color.BLACK);
        paint.setStrokeCap(Paint.Cap.ROUND);
        //(r-150)就是分针的长度
        canvas.drawLine(x, y, (int) (x + Math.sin(angleMinute) * (r - 150)), (int) (y - Math.cos(angleMinute) * (r - 150)), paint);
        //画秒针
        paint.setStrokeWidth(10);
        paint.setColor(Color.BLUE);
        paint.setStrokeCap(Paint.Cap.ROUND);
        //(r-100)就是秒针的长度
        canvas.drawLine(x, y, (int) (x + Math.sin(angleSecond) * (r - 100)), (int) (y - Math.cos(angleSecond) * (r - 100)), paint);
    }
}

Activity

java 复制代码
import androidx.appcompat.app.AppCompatActivity;;
import android.os.Bundle;

public class CanvasActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_canvas);
    }
}

布局文件

html 复制代码
<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".CanvasActivity">
    <com.example.sim.view.ClockView
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>
相关推荐
帅次14 分钟前
Android CoordinatorLayout:打造高效交互界面的利器
android·gradle·android studio·rxjava·android jetpack·androidx·appcompat
枯骨成佛1 小时前
Android中Crash Debug技巧
android
kim56596 小时前
android studio 更改gradle版本方法(备忘)
android·ide·gradle·android studio
咸芝麻鱼6 小时前
Android Studio | 最新版本配置要求高,JDK运行环境不适配,导致无法启动App
android·ide·android studio
无所谓จุ๊บ6 小时前
Android Studio使用c++编写
android·c++
csucoderlee7 小时前
Android Studio的新界面New UI,怎么切换回老界面
android·ui·android studio
kim56597 小时前
各版本android studio下载地址
android·ide·android studio
饮啦冰美式7 小时前
Android Studio 将项目打包成apk文件
android·ide·android studio
夜色。7 小时前
Unity6 + Android Studio 开发环境搭建【备忘】
android·unity·android studio
ROCKY_8179 小时前
AndroidStudio-滚动视图ScrollView
android