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