前言:
钟表作为我们日常随处可见的生活物品,在过去几十年里,起到了很重要的标记时间的作用。如今,随着智能手机的普及,它更作为装饰品出现在房间。那么在学完Java,我们能否用程序实现一个图形化的钟表呢?
代码:
import java.awt.*;
import java.awt.geom.Line2D;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Clock extends JPanel implements Runnable {
private Thread thread=new Thread(this);
private SimpleDateFormat dateFormat;
private Point center;
public Clock() {
//设置面板尺寸
setPreferredSize(new Dimension(200, 200));
//设置背景颜色
setBackground(Color.WHITE);
//格式化时间
dateFormat = new SimpleDateFormat("hh:mm:ss");
//创建一个Point类,表示圆心
center = new Point(100, 100);
//启动线程
thread.start();
}
//线程保证时钟运行状态不断更新
@Override
public void run() {
while (true) {
try {
//每一秒钟更新重绘一次
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
repaint();
}
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
//创建一个g2d画笔
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//Calendar是一个抽象类,所以通过getInstance()方法来获取Calendar对象
Calendar now = Calendar.getInstance();
//获取时间
int hour = now.get(Calendar.HOUR);
int minute = now.get(Calendar.MINUTE);
int second = now.get(Calendar.SECOND);
//绘制表面
drawClockFace(g2d);
//绘制钟表的指针
drawHand(g2d, (hour + minute / 60.0) * 30, 50, Color.BLACK, 6);
drawHand(g2d, minute * 6, 80, Color.BLUE, 4);
drawHand(g2d, second * 6, 90, Color.RED, 2);
//释放资源
g2d.dispose();
}
//绘制表面的方法
private void drawClockFace(Graphics2D g2d) {
g2d.setColor(Color.BLACK);
//设置画笔粗细
g2d.setStroke(new BasicStroke(2));
Font font = new Font("黑体", Font.PLAIN, 12);
g2d.setFont(font);
//绘制刻度和数字
for (int i = 1; i <= 60; i++) {
//将角度制转化为弧度制
double angle = i * 6 * Math.PI / 180;
//如果是5的倍数则说明是小时数字,刻度线需要更长一些
int length = i % 5 == 0 ? 10 : 5;
//计算刻度线的坐标。
//得到角度的三角函数值,再乘以半径,得到刻度线的起点和终点坐标
int x1 = (int) (center.x + Math.sin(angle) * 80);
int y1 = (int) (center.y - Math.cos(angle) * 80);
int x2 = (int) (center.x + Math.sin(angle) * (80 - length));
int y2 = (int) (center.y - Math.cos(angle) * (80 - length));
if (i % 5 == 0) {
String str = Integer.toString(i / 5);
//获取字符串的高度和宽度
int strWidth = g2d.getFontMetrics().stringWidth(str);
int strHeight = g2d.getFontMetrics().getAscent();
//将字符串画在表盘上
g2d.drawString(str, x2 - strWidth / 2, y2 + strHeight / 2);
}
//将刻度线的始末坐标用线连起来
g2d.draw(new Line2D.Double(x1, y1, x2, y2));
}
//绘制一个圆
g2d.drawOval(center.x - 90, center.y - 90, 180, 180);
}
//绘制指针的方法
private void drawHand(Graphics2D g2d, double angle, int length, Color color, int width) {
g2d.setColor(color);
g2d.setStroke(new BasicStroke(width));
//将角度转换为弧度,并计算出指针末尾的坐标
int x2 = (int) (center.x + Math.sin(angle*Math.PI / 180) * length);
int y2 = (int) (center.y - Math.cos(angle*Math.PI / 180) * length);
//绘制从钟表中心到指针末尾的直线
g2d.drawLine(center.x, center.y, x2, y2);
}
public static void main(String[] args) {
JFrame frame = new JFrame("Clock");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new Clock());
frame.setLocation(600,300);
//自动调整窗口大小
frame.pack();
frame.setVisible(true);
}
}
思路分析:
既然我们要实现一个钟表,我们首先先要知道我们要做什么,捋清思路。
- 如何绘制表盘?
- 如何绘制指针?
- 如何让指针正确地转动?
总的来说,我们只需要解决以上3个问题,实现一个钟表便轻而易举。接下来,我们进行逐一解决。
如何绘制表盘?
首先,一个表盘,需要画一个圆,然后再在圆上画出相应的刻度线。
圆很好画,直接使用 g2d自带的drawOval()方法,规定圆心坐标及半径即可。
接下来,开始画刻度线。一圈有60个刻度线,12个大刻度线(每5个刻度就有一个大刻度线,所以可以直接遍历i%5),其余的都是小刻度线,我们可以直接遍历从i=1到i=60。
可是,如何才能把它绕一圈画在圆上?每个刻度线的间隔是360/60=6°,也就是每6°要画一个刻度线,我们可以找出该刻度线的起始坐标和末端坐标,然后用drawLine()方法,连接两个坐标。
下一个问题,如何计算出刻度线对应的坐标?利用简单的三角函数,我画了个图。
设置好圆心的坐标后,可以在直角三角形里,通过三角函数值和半径计算出Δx和Δy,根据圆心坐标一次计算出所有刻度线的起始坐标和末坐标,最后通过将起始坐标和末坐标连起来。这样,刻度线就绘画完成。
具体这样实施:
private void drawClockFace(Graphics2D g2d) {
g2d.setColor(Color.BLACK);
//设置画笔粗细
g2d.setStroke(new BasicStroke(2));
Font font = new Font("黑体", Font.PLAIN, 12);
g2d.setFont(font);
//绘制刻度和数字
for (int i = 1; i <= 60; i++) {
//将角度制转化为弧度制
double angle = i * 6 * Math.PI / 180;
//如果是5的倍数则说明是小时数字,刻度线需要更长一些
int length = i % 5 == 0 ? 10 : 5;
//计算刻度线的坐标。
//得到角度的三角函数值,再乘以半径,得到刻度线的起点和终点坐标
int x1 = (int) (center.x + Math.sin(angle) * 80);
int y1 = (int) (center.y - Math.cos(angle) * 80);
int x2 = (int) (center.x + Math.sin(angle) * (80 - length));
int y2 = (int) (center.y - Math.cos(angle) * (80 - length));
if (i % 5 == 0) {
String str = Integer.toString(i / 5);
//获取字符串的高度和宽度
int strWidth = g2d.getFontMetrics().stringWidth(str);
int strHeight = g2d.getFontMetrics().getAscent();
//将字符串画在表盘上
g2d.drawString(str, x2 - strWidth / 2, y2 + strHeight / 2);
}
//将刻度线的始末坐标用线连起来
g2d.draw(new Line2D.Double(x1, y1, x2, y2));
}
//绘制一个圆
g2d.drawOval(center.x - 90, center.y - 90, 180, 180);
}
如何绘制指针?
其实指针的绘制和刻度线的绘制方法类似。利用计算坐标,再将坐标和圆心相连,即可画出指针,不同的指针可以通过指针粗细和颜色来区分。 具体实施代码如下:
private void drawHand(Graphics2D g2d, double angle, int length, Color color, int width) {
g2d.setColor(color);
g2d.setStroke(new BasicStroke(width));
//将角度转换为弧度,并计算出指针末尾的坐标
int x2 = (int) (center.x + Math.sin(angle*Math.PI / 180) * length);
int y2 = (int) (center.y - Math.cos(angle*Math.PI / 180) * length);
//绘制从钟表中心到指针末尾的直线
g2d.drawLine(center.x, center.y, x2, y2);
}
通过改变传入方法参数的不同,来决定是什么指针。
如何让指针正确地转动?
我们首先需要通过Calendar类来获取当前日历,得到当前的小时、分钟和秒钟。然后依此绘制指针。
秒针:秒针一秒只走6°,所以:
drawHand(g2d, second * 6, 90, Color.RED, 2);
分针:和秒针一样,一分钟走6°,所以:
drawHand(g2d, minute * 6, 80, Color.BLUE, 4);
时针:因为时针和其它两个不同,它每次转动一定是转过5个刻度线,也就是30°,所以:
drawHand(g2d, (hour + minute / 60.0) * 30, 50, Color.BLACK, 6);
最后,指针应该是一直转动的,我们可以通过一个单线程在死循环内不断地重绘表盘即可。
@Override public void run() { while (true) { try { //每一秒钟更新重绘一次 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } repaint(); } }
如此,用Java实现一个简单的图形化钟表就完成啦!
我们用到的知识不多,包括swing和线程。
难一点的主要还是在绘制表盘和指针那里的算法。所以,要多刷算法题哦qaq。