一个精美的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/10367.html

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

相关推荐

发表回复

登录后才能评论