1. 程式人生 > >java視覺化程式設計實踐--桌面時鐘

java視覺化程式設計實踐--桌面時鐘

定義六個類用於實現整個視覺化時鐘
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