定义六个类用于实现整个可视化时钟
Clock.java:时钟类,聚合表盘和3个指针对象,构成整个时钟
Plate.java:表盘类,用来显示静态的时钟表盘、刻度等内容
Arm.java:指针类,用来显示时、分、秒指针
ClockComponent:可视化时钟组件类,时钟的可视化显示,即整个时钟界面的表盘显示。
ClockFrame:整个程序窗口类,用于显示整个程序的窗口。
VisualClock:主程序类,用于程序的启动。
主要的程序如下:
VisualClock:
import java.awt.EventQueue;
import javax.swing.JFrame;
/*顶层窗口 JFrame 内容区域包括 2 个部分,上方为时钟的可视化显示,下方
为文本显示,上方用 JComponent 组件实现,在其 paintComponent 方法中绘制,
下方用 JLabel 实现,可以采用 BorderLayout 布局实现
*/
//添加主程序类
public class VisualClock {
public static void main(String[] args) {
EventQueue.invokeLater(()->{
ClockFrame frame=new ClockFrame();
frame.setTitle("可视化时钟");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE );
frame.setVisible(true);
});
}
}
ClockFrame:
import java.awt.*;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import javax.swing.*;
import java.util.Date;
//创建顶层窗口类
public class ClockFrame extends JFrame {
private ClockComponent comp;//整个时钟的显示
private JLabel timeLabel;//时钟的文本显示
public ClockFrame() {
setSize(500,400);
setLayout(new BorderLayout());
comp=new ClockComponent();
add(comp, BorderLayout.CENTER);
JPanel timeLabelPanel=new JPanel();
timeLabel=new JLabel(""+new Date());
timeLabelPanel.add(timeLabel);
add(timeLabelPanel, BorderLayout.SOUTH);
//创建定时器并通过start启动定时器,每隔一秒根据当前系统生成的时间
//生成字符串,将字符串更新到timeLabel上,同时调用repaint方法刷新comp显示
new Timer(1000,event->{
Date date=new Date();
DateFormat format=new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");
String time=format.format(date);
timeLabel.setText(time);
comp.repaint();
}).start();
}
}
ClockComponent:
import javax.swing.*;
import java.awt.*;
import java.awt.geom.Rectangle2D;
//添加可视化时钟组件类
public class ClockComponent extends JComponent {
//时钟的可视化显示,即整个时钟界面的表盘显示
Clock clock,clock1;
public ClockComponent(){
clock=new Clock();
clock1=new Clock();
}
public void paintComponent(Graphics g){
Rectangle2D r=getBounds();
Graphics2D g2=(Graphics2D)g;
clock.setRectangle(new Rectangle2D.Double(r.getX(),r.getY(),r.getWidth()/2,r.getHeight()));//通过 getBounds 方法获得组件窗体的大小,并将期传给 clock 对象,再间接传递给 plate 对象
clock1.setRectangle(new Rectangle2D.Double(r.getWidth()/2,r.getY(),r.getWidth()/2,r.getHeight()));
clock.draw(g2);//Graphics2D对象也是由 clock 的 draw 方法传递给 plate 的 draw 方法,从而将表盘绘制在Component 组件中。
clock1.draw1(g2);
}
}
Clock:
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
public class Clock {
//时钟类,聚合表盘和3个指针对象,构成整个时钟
private Plate plate;
private Arm hour,minute,second;
public Clock(){
plate=new Plate();//构造表盘对象
//调用指针的创建与绘制
hour=new Arm(Arm.Type.HOUR);
minute=new Arm(Arm.Type.MINUTE);
second=new Arm(Arm.Type.SECOND);
hour.setColor(Color.red);
minute.setColor(Color.BLUE);
second.setColor(Color.green);
}
public void setRectangle(Rectangle2D rect){
plate.setRectangle(rect);//设置外界矩形大小
//plate.setRectangle(rect);
hour.setRectangle(rect);
minute.setRectangle(rect);
second.setRectangle(rect);
}
public void draw(Graphics2D g2){
plate.draw(g2);//绘制表盘
hour.draw(g2);
minute.draw(g2);
second.draw(g2);
}
public void draw1(Graphics2D g2){
plate.draw(g2);//绘制表盘
hour.draw1(g2);
minute.draw1(g2);
second.draw1(g2);
}
}
Plate:
import java.awt.*;
import java.awt.geom.*;
public class Plate {
//表盘类,用来显示静态的时钟表盘、刻度等内容
public static final int LINE_LENGTH=9; //刻度长度
public static final float LINE_WIDTH=3.0f; //线宽
public static final int GAP=10; //保留和边界的距离
private Color backgroundColor=Color.LIGHT_GRAY;
private Color lineColor=Color.BLACK;
private Color numColor=Color.RED; //不同对象的颜色
private Rectangle2D r; //控制表盘大小的外接矩形
void setRectangle(Rectangle2D rect){
r=rect;
}
/*
Plate 类封装了对表盘的绘制,通过 draw 接口,将表盘绘制在传入的
Graphics2D 对象上,由于 Plate 本身无法确定表盘的大小,需要从外部设置外接
矩形的参数。在程序运行过程中,窗口大小发生变化时,需要调用 setRectangle
接口重新设置矩形大小。
*/
void draw(Graphics2D g2) {
//外部传入的绘制对象
double width=r.getWidth();//获得表盘外界矩形的宽
double height=r.getHeight();
double minValue=Math.min(width, height); //长宽最小值
double x=r.getX()+width/2;
double y=r.getY()+height/2; //中心点 x、y 坐标
double radius=minValue/2-GAP; //计算半径
Ellipse2D.Double ellipse=new Ellipse2D.Double(x-radius,y-radius,radius*2,radius*2);
g2.setColor(backgroundColor); //设置背景颜色
g2.fill(ellipse); //填充表盘背景
g2.setColor(lineColor);
Stroke stroke=new BasicStroke(LINE_WIDTH); //设置线宽
g2.setStroke(stroke);
g2.draw(ellipse); //绘制表盘边框线
//g2.draw(new Line2D.Double(x-radius,y,x-radius+LINE_LENGTH,y));
//g2.draw(new Line2D.Double(x+radius,y,x+radius-LINE_LENGTH,y));
//g2.draw(new Line2D.Double(x,y-radius,x,y-radius+LINE_LENGTH));
//g2.draw(new Line2D.Double(x,y+radius,x,y+radius-LINE_LENGTH)); //绘制刻度线,还有刻度值、部分刻度线还没有完成,需要继续补充
for(int i=0;i<12;++i){//循环实现刻度线
double size=4;
if(i%3==0) size=8;
drawMark(g2,x,y,radius,size,i);
}
//刻度值3,6,9,12的输出
g2.drawString("12", (int)x - 5, (int)y- (int)radius + 20);
g2.drawString("9", (int)x - (int)radius + 11, (int)y + 5);
g2.drawString("3", (int)x + (int)radius - 18, (int)y + 3);
g2.drawString("6", (int)x - 3, (int)y + (int)radius - 11);
}
private void drawMark(Graphics2D g2,double x,double y,double radius,double size,int index) {
double angle=90;
angle-=index*30;
double x1=x+radius*Math.cos(Math.PI*angle/180);
double y1=y-radius*Math.sin(Math.PI*angle/180);
radius-=size;
double x2=x+radius*Math.cos(Math.PI*angle/180);
double y2=y-radius*Math.sin(Math.PI*angle/180);
g2.draw(new Line2D.Double(x1, y1, x2, y2));
}
}
Arm:
import java.awt.geom.*;
import java.time.LocalTime;
import java.awt.*;
public class Arm {
//指针类,用来显示时、分、秒指针
public enum Type{HOUR,MINUTE,SECOND}; //枚举类型,指针类型
private Type type; //指针类型
private Color armColor=Color.BLUE; //指针颜色
private Rectangle2D r; //外接矩形大小
public Arm(Type t){
type=t;
}
public void setRectangle(Rectangle2D rect){
r=rect;
}
public void setColor(Color c){
armColor=c;
}
public void draw(Graphics2D g2) {
LocalTime time=LocalTime.now(); //获取系统当前时间
int h=time.getHour()%12;
int m=time.getMinute();
int s=time.getSecond();
double angle=90,size=0;
double x=r.getX()+r.getWidth()/2;
double y=r.getY()+r.getHeight()/2; //计算中心点
size=Math.min(r.getWidth()/2, r.getHeight()/2);
switch(type){ //根据指针类型、时间计算角度和长度
case HOUR:
angle=90-h*30-m/2;
size*=18.0/30;
break;
case MINUTE:
angle=90-m*6-s/10;
size*=20.0/30;
break;
case SECOND:
angle=90-s*6;
size*=23.0/30;
break;
}
g2.setColor(armColor);
//double x1=x+size*Math.cos(Math.PI*angle/180);
//double y1=y-size*Math.sin(Math.PI*angle/180); //计算末端坐标
//g2.draw(new Line2D.Double(x, y, x1, y1));
//换成三角状的指针
int[] xpoint=new int[5];
int[] ypoint=new int[5];
double angle1=angle-90;
double size1=size/10;
double size2=size/5;
double dy1=size1*Math.sin(Math.PI*angle1/180);
double dx1=size1*Math.cos(Math.PI*angle1/180);
xpoint[0]=(int)(x+dx1);
ypoint[0]=(int)(y-dy1);
double dy2=size*Math.sin(Math.PI*angle/180);
double dx2=size*Math.cos(Math.PI*angle/180);
xpoint[1]=(int)(x+dx2);
ypoint[1]=(int)(y-dy2);
xpoint[2]=(int)(x-dx1);
ypoint[2]=(int)(y+dy1);
double dy3=size2*Math.sin(Math.PI*angle/180);
double dx3=size2*Math.cos(Math.PI*angle/180);
xpoint[3]=(int)(x-dx3);
ypoint[3]=(int)(y+dy3);
xpoint[4]=xpoint[0];
ypoint[4]=ypoint[0];
Polygon poly=new Polygon(xpoint,ypoint,5);
g2.fillPolygon(poly);
final double radius=5;
Ellipse2D.Double ellipse=new Ellipse2D.Double(x-radius,y-radius,radius*2,radius*2);
g2.setColor(Color.BLACK);
g2.fill(ellipse); //绘制中心圆点
}
public void draw1(Graphics2D g2) {
LocalTime time=LocalTime.now(); //获取系统当前时间
int h=time.getHour()%12-11;
int m=time.getMinute();
int s=time.getSecond();
double angle=90,size=0;
double x=r.getX()+r.getWidth()/2;
double y=r.getY()+r.getHeight()/2; //计算中心点
size=Math.min(r.getWidth()/2, r.getHeight()/2);
switch(type){ //根据指针类型、时间计算角度和长度
case HOUR:
angle=90-h*30-m/2;
size*=18.0/30;
break;
case MINUTE:
angle=90-m*6-s/10;
size*=20.0/30;
break;
case SECOND:
angle=90-s*6;
size*=23.0/30;
break;
}
g2.setColor(armColor);
//double x1=x+size*Math.cos(Math.PI*angle/180);
//double y1=y-size*Math.sin(Math.PI*angle/180); //计算末端坐标
//g2.draw(new Line2D.Double(x, y, x1, y1));
//换成三角状的指针
int[] xpoint=new int[5];
int[] ypoint=new int[5];
double angle1=angle-90;
double size1=size/10;
double size2=size/5;
double dy1=size1*Math.sin(Math.PI*angle1/180);
double dx1=size1*Math.cos(Math.PI*angle1/180);
xpoint[0]=(int)(x+dx1);
ypoint[0]=(int)(y-dy1);
double dy2=size*Math.sin(Math.PI*angle/180);
double dx2=size*Math.cos(Math.PI*angle/180);
xpoint[1]=(int)(x+dx2);
ypoint[1]=(int)(y-dy2);
xpoint[2]=(int)(x-dx1);
ypoint[2]=(int)(y+dy1);
double dy3=size2*Math.sin(Math.PI*angle/180);
double dx3=size2*Math.cos(Math.PI*angle/180);
xpoint[3]=(int)(x-dx3);
ypoint[3]=(int)(y+dy3);
xpoint[4]=xpoint[0];
ypoint[4]=ypoint[0];
Polygon poly=new Polygon(xpoint,ypoint,5);
g2.fillPolygon(poly);
final double radius=5;
Ellipse2D.Double ellipse=new Ellipse2D.Double(x-radius,y-radius,radius*2,radius*2);
g2.setColor(Color.BLACK);
g2.fill(ellipse); //绘制中心圆点
}
}
最后可以在Eclipse中建立一个包文件,将这些类放入其中即可。
程序运行结果如下:
两个时钟:第一个是中国时间,第二个是美国纽约时间
如果只想在界面中显示一个时钟的话,直接在ClockComponent类中删除一个时钟定义即可。
做这个时钟界面时发现一个实现的比较基础的可视化时钟程序,感觉也挺好的:
https://blog.csdn.net/qq_24653023/article/details/52195190