一个精美的java时钟 (自动换背景 秒针平滑移动)详解编程语言

import java.awt.BasicStroke; 
import java.awt.Color; 
import java.awt.Container; 
import java.awt.Font; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.Image; 
import java.awt.MediaTracker; 
import java.awt.Toolkit; 
import java.awt.geom.Ellipse2D; 
import java.awt.geom.Line2D; 
import java.awt.image.BufferedImage; 
import java.net.URL; 
import java.text.DateFormat; 
import java.util.Calendar; 
import java.util.Date; 
import java.util.GregorianCalendar; 
 
import javax.swing.JFrame; 
import javax.swing.JLabel; 
 
/** 
 * 一个精美的时钟 (自动换背景 秒针平滑移动) 
 *  
 * 注意:背景图片需放在该包的image目录下(命名:1.jpg...5.jpg) 
 */ 
public class Clock extends JFrame { 
	// 今天的日期对象 
	private GregorianCalendar now = new GregorianCalendar(); 
	// 时钟标签,上面画的是圆形时钟 
	private ClockLabel clockLabel = new ClockLabel(); 
	// 星期标签,指示星期 
	private JLabel weekLabel = new JLabel(); 
	// 日期标签,指示日期 
	private JLabel dateLabel = new JLabel(); 
	// 品牌标签 
	private JLabel remarkLabel = new JLabel(); 
	// 时间标签,指示时间 
	private JLabel timeLabel = new JLabel(); 
 
	public Clock() { 
 
		setTitle("时钟"); 
		setSize(500, 480); 
		setLocation(((int) Toolkit.getDefaultToolkit().getScreenSize() 
				.getWidth() - 500) / 2, ((int) Toolkit.getDefaultToolkit() 
				.getScreenSize().getHeight() - 480) / 2); 
		init(); 
		setResizable(false); 
	} 
 
	private void init() { 
 
		// 初始化品牌标签 
		remarkLabel.setText("MyClock"); 
		remarkLabel.setLocation(205, 80); 
		remarkLabel.setSize(100, 30); 
		remarkLabel.setFont(new Font("Bookman Old Style", Font.BOLD, 20)); 
		remarkLabel.setForeground(Color.orange); 
 
		// 初始化星期标签 
		weekLabel.setSize(60, 20); 
		weekLabel.setLocation(315, 190); 
		weekLabel.setForeground(Color.pink); 
		weekLabel.setFont(new Font("Arial Narrow", Font.BOLD, 12)); 
		// 为星期标签赋值 
		int week = now.get(Calendar.DAY_OF_WEEK); 
		switch (week) { 
		case 1: 
			weekLabel.setText("SUNDAY"); 
			break; 
		case 2: 
			weekLabel.setText("MONDAY"); 
			break; 
		case 3: 
			weekLabel.setText("TUESDAY"); 
			break; 
		case 4: 
			weekLabel.setText("WEDNESDAY"); 
			break; 
		case 5: 
			weekLabel.setText("THURSDAY"); 
			break; 
		case 6: 
			weekLabel.setText("FRIDAY"); 
			break; 
		case 7: 
			weekLabel.setText("SATURDAY"); 
			break; 
		} 
 
		// 初始化日期标签 
		dateLabel.setSize(20, 20); 
		dateLabel.setLocation(375, 190); 
		dateLabel.setForeground(Color.pink); 
		dateLabel.setFont(new Font("Fixedsys", Font.BOLD, 12)); 
		dateLabel.setText("" + now.get(Calendar.DATE)); 
 
		// 初始化时间标签 
		timeLabel.setSize(500, 30); 
		timeLabel.setLocation(100, 400); 
		timeLabel.setForeground(new Color(0, 64, 128)); 
		timeLabel.setFont(new Font("Fixedsys", Font.PLAIN, 15)); 
 
		// 将各组件加入到主窗口中 
		Container con = getContentPane(); 
		con.setBackground(Color.white); 
		con.setLayout(null); 
		con.add(weekLabel); 
		con.add(dateLabel); 
		con.add(remarkLabel); 
		con.add(timeLabel); 
		con.add(clockLabel); 
	} 
 
	public static void main(String[] args) { 
		Clock clock = new Clock(); 
		clock.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
		clock.setVisible(true); 
	} 
 
	// 自定义时钟标签,画一个圆形的时钟 
	class ClockLabel extends JLabel implements Runnable { 
		// 时钟标签的宽度和高度 
		private final int WIDTH = 500; 
		private final int HEIGHT = 440; 
 
		// 圆形时钟的X半径和Y半径 
		private final int CIRCLE_X_RADIUS = 150; 
		private final int CIRCLE_Y_RADIUS = 155; 
 
		// 圆形时钟的原点 
		private final int CIRCLE_X = 250; 
		private final int CIRCLE_Y = 200; 
 
		// 圆形时钟指针的长度 
		private final int HOUR_LENGTH = 70; 
		private final int MIN_LENGTH = 100; 
		private final int SEC_LENGTH = 135; 
 
		// 当前时针所处的角度 
		double arcHour = 0; 
		// 当前分针所处的角度 
		double arcMin = 0; 
		// 当前秒针所处的角度 
		double arcSec = 0; 
 
		// 颜色的透明度, 
		int alpha = 60; 
		// 标识颜色透明度变化的方向,为true时表示越来越透明,为false时表示月来越不透明 
		boolean flag = false; 
		// 背景图片的id,轮换显示背景图片时使用 
		int imageID = 0; 
		// 背景图片对象数组 
		Image img[] = new Image[5]; 
		// 背景图片URL,在该包的image目录下(命名:1.jpg...5.jpg) 
		URL url[] = new URL[] { ClockLabel.class.getResource("image/1.jpg"), 
				ClockLabel.class.getResource("image/2.jpg"), 
				ClockLabel.class.getResource("image/3.jpg"), 
				ClockLabel.class.getResource("image/4.jpg"), 
				ClockLabel.class.getResource("image/5.jpg"), 
				ClockLabel.class.getResource("image/6.jpg") }; 
 
		// 一个具有缓冲区的图像对象 
		BufferedImage bufferedImage = null; 
		int imageSize = 2 * Math.max(CIRCLE_X_RADIUS, CIRCLE_Y_RADIUS); 
		// 为bufferedImage创建的Graphics对象,用于在bufferedImage上画图 
		Graphics bufferedImageGraphics = null; 
		// 时钟线程 
		Thread clockThread = null; 
		// 计数器 
		int count = 0; 
 
		public ClockLabel() { 
 
			// 设置时钟标签的大小 
			this.setSize(WIDTH, HEIGHT); 
 
			// 获取时针、分针、秒针当前的角度 
			arcHour = now.get(Calendar.HOUR) * (360.0 / 12) 
					+ now.get(Calendar.MINUTE) * (360.0 / 12 / 60) 
					+ now.get(Calendar.SECOND) * (360.0 / 12 / 60 / 60); 
			arcMin = now.get(Calendar.MINUTE) * (360.0 / 60) 
					+ now.get(Calendar.SECOND) * (360.0 / 60 / 60); 
			arcSec = now.get(Calendar.SECOND) * (360.0 / 60); 
 
			// 根据图片URL创建图片对象 
			Toolkit tk = this.getToolkit(); 
			img[0] = tk.createImage(url[0]); 
			img[1] = tk.createImage(url[1]); 
			img[2] = tk.createImage(url[2]); 
			img[3] = tk.createImage(url[3]); 
			img[4] = tk.createImage(url[4]); 
			try { 
				// 使用MediaTracker加载图片对象 
				// MediaTracker 类是一个跟踪多种媒体对象状态的实用工具类, 
				// 媒体对象可以包括音频剪辑和图像,但目前仅支持图像. 
				MediaTracker mt = new MediaTracker(this); 
				mt.addImage(img[0], 0); 
				mt.addImage(img[1], 0); 
				mt.addImage(img[2], 0); 
				mt.addImage(img[3], 0); 
				mt.addImage(img[4], 0); 
				// 加载媒体跟踪器中所有的图像。 
				mt.waitForAll(); 
			} catch (Exception e) { 
				e.printStackTrace(); 
			} 
 
			// 创建一个有缓冲的Image对象 
			bufferedImage = new BufferedImage(imageSize, imageSize, 
					BufferedImage.TYPE_INT_ARGB); 
			// 为BufferedImage创建Graphics2D对象, 
			// 以后用该Graphics2D对象画的图都会画在BufferedImage中 
			bufferedImageGraphics = bufferedImage.createGraphics(); 
 
			// 启动线程 
			clockThread = new Thread(this); 
			clockThread.start(); 
		} 
 
		public void paint(Graphics g1) { 
			// Graphics2D继承Graphics,比Graphics提供更丰富的方法 
			Graphics2D g = (Graphics2D) g1; 
 
			/** ***画圆形时钟的刻度,每6度便有一个刻度**** */ 
			for (int i = 0; i < 360; i = i + 6) { 
				g.setColor(Color.gray); 
				// 设置画笔的宽度为2 
				g.setStroke(new BasicStroke(2)); 
 
				// 画刻度 
				if (i % 90 == 0) { 
					// 对于0,3,6,9点位置,使用一个大的刻度 
					g.setColor(Color.pink); 
					g.setStroke(new BasicStroke(7));// 画笔宽度为5 
					// 当起点和终点一样时,画的就是点 
					g.drawLine( 
							CIRCLE_X 
									+ (int) (Math.cos(i * Math.PI / 180) * CIRCLE_X_RADIUS), 
							CIRCLE_Y 
									+ (int) (Math.sin(i * Math.PI / 180) * CIRCLE_Y_RADIUS), 
							CIRCLE_X 
									+ (int) (Math.cos(i * Math.PI / 180) * CIRCLE_X_RADIUS), 
							CIRCLE_Y 
									+ (int) (Math.sin(i * Math.PI / 180) * CIRCLE_Y_RADIUS)); 
				} else if (i % 30 == 0) { 
					// 如果角度处于小时的位置,而且还不在0,3,6,9点时,画红色的小刻度 
					g.setColor(Color.orange); 
					g.setStroke(new BasicStroke(3));// 画笔宽度为3 
					g.drawLine( 
							CIRCLE_X 
									+ (int) (Math.cos(i * Math.PI / 180) * CIRCLE_X_RADIUS), 
							CIRCLE_Y 
									+ (int) (Math.sin(i * Math.PI / 180) * CIRCLE_Y_RADIUS), 
							CIRCLE_X 
									+ (int) (Math.cos(i * Math.PI / 180) * CIRCLE_X_RADIUS), 
							CIRCLE_Y 
									+ (int) (Math.sin(i * Math.PI / 180) * CIRCLE_Y_RADIUS)); 
				} else { 
					// 其他位置就画小刻度 
					g.setColor(Color.gray); 
					g.drawLine( 
							CIRCLE_X 
									+ (int) (Math.cos(i * Math.PI / 180) * CIRCLE_X_RADIUS), 
							CIRCLE_Y 
									+ (int) (Math.sin(i * Math.PI / 180) * CIRCLE_Y_RADIUS), 
							CIRCLE_X 
									+ (int) (Math.cos(i * Math.PI / 180) * CIRCLE_X_RADIUS), 
							CIRCLE_Y 
									+ (int) (Math.sin(i * Math.PI / 180) * CIRCLE_Y_RADIUS)); 
				} 
			} 
 
			/** ****** 画时钟的指针 ******** */ 
			// 画时针 
			Line2D.Double lh = new Line2D.Double(CIRCLE_X, CIRCLE_Y, CIRCLE_X 
					+ Math.cos((arcHour - 90) * Math.PI / 180) * HOUR_LENGTH, 
					CIRCLE_Y + Math.sin((arcHour - 90) * Math.PI / 180) 
							* HOUR_LENGTH); 
			// 设置画笔宽度和颜色 
			g.setStroke(new BasicStroke(8)); 
			g.setColor(Color.pink); 
			// 利用Graphics2D的draw方法画线 
			g.draw(lh); 
 
			// 画分针 
			Line2D.Double lm = new Line2D.Double(CIRCLE_X, CIRCLE_Y, CIRCLE_X 
					+ Math.cos((arcMin - 90) * Math.PI / 180) * MIN_LENGTH, 
					CIRCLE_Y + Math.sin((arcMin - 90) * Math.PI / 180) 
							* MIN_LENGTH); 
			g.setStroke(new BasicStroke(4)); 
			g.setColor(Color.orange); 
			g.draw(lm); 
 
			// 画秒针 
			Line2D.Double ls = new Line2D.Double(CIRCLE_X, CIRCLE_Y, CIRCLE_X 
					+ Math.cos((arcSec - 90) * Math.PI / 180) * SEC_LENGTH, 
					CIRCLE_Y + Math.sin((arcSec - 90) * Math.PI / 180) 
							* SEC_LENGTH); 
			g.setStroke(new BasicStroke(1)); 
 
			g.setColor(Color.lightGray); 
			g.draw(ls); 
 
			/** **** 画时钟背景,并将其透明处理 ******* */ 
			// 所有使用bufferedImageGraphics画的图像,都画在bufferedImage上, 
			// drawImage方法的参数含义分别是:背景图片对象、目标位置第一个角的X、Y坐标、目标位置第二个角的X、Y坐标、 
			// 源位置第一个角的X、Y坐标、源位置第二个角的X、Y坐标、图像对象改变时的通知接受者 
			bufferedImageGraphics.drawImage(img[imageID], 0, 0, imageSize, 
					imageSize, 0, 0, imageSize + 10, imageSize + 10, this); 
			// 将背景图片透明化 
			for (int j = 0; j < imageSize; j++) { 
				for (int i = 0; i < imageSize; i++) { 
					// 获得背景图像中(i, j)坐标的颜色值 
					int pix = bufferedImage.getRGB(i, j); 
					Color c = new Color(pix); 
					int R = c.getRed(); 
					int G = c.getGreen(); 
					int B = c.getBlue(); 
					// 通过Color对象的alpha,使颜色透明。 
					int newpix = new Color(R, G, B, alpha).getRGB(); 
					// 重新设置背景图像该象素点的颜色 
					bufferedImage.setRGB(i, j, newpix); 
				} 
			} 
 
			/** 以上画背景操作都是画在bufferedImage上的,这里要将bufferedImage画在ClockLabel **/ 
			// 将当前用户剪贴区 Clip 与椭圆区域相交,并将 Clip 设置为所得的交集 
			g.clip(new Ellipse2D.Double(95, 45, imageSize, imageSize)); 
			g.setColor(Color.white); 
			// 在用户剪贴区画bufferedImage 
			g.drawImage(bufferedImage, 95, 45, this); 
		} 
 
		public void run() { 
			try { 
 
				while (clockThread != null) { 
 
					// 计数 
					count++; 
					// 获得完整的日期格式 
					DateFormat df = DateFormat.getDateTimeInstance( 
							DateFormat.FULL, DateFormat.FULL); 
					// 格式化当前时间 
					String s = df.format(new Date()); 
					timeLabel.setText(s); 
					// 每动一次对时钟指针的角度进行调整 
					arcSec += 360.0 / 60 / 10; // 每秒转6度 
					arcMin += 360.0 / 60 / 60 / 10; // 每60秒转6度 
					arcHour += 360.0 / 12 / 60 / 60 / 10; // 每3600秒转30度 
 
					// 当角度满一圈时,归0 
					if (arcSec >= 360) { 
						arcSec = 0; 
					} 
					if (arcMin >= 360) { 
						arcMin = 0; 
					} 
					if (arcHour >= 360) { 
						arcHour = 0; 
					} 
					// 实现背景透明度渐变 
					// 从浅入深,再由深入浅。 
					if (count % 2 == 0) {// 用于背景替换减速 
						if (flag) { 
							alpha += 1; 
							if (alpha == 60) { 
								flag = !flag; 
							} 
						} else { 
							alpha -= 1; 
							if (alpha == 0) { 
								flag = !flag; 
								// 当透明度变化一个轮回时,换一张背景图 
								imageID++; 
								if (imageID == 4) { 
									imageID = 0; 
								} 
							} 
						} 
						if (count >= 2147483647) { // 防溢出 
							count = 0; 
						} 
					} 
					// 重画时钟标签 
					repaint(); 
 
					// 等待0.1秒钟 
					Thread.sleep(100); 
 
				} 
			} catch (Exception e) { 
				e.printStackTrace(); 
			} 
		} 
	} 
}

原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/10367.html

(0)
上一篇 2021年7月19日 10:11
下一篇 2021年7月19日 10:11

相关推荐

发表回复

登录后才能评论