Java 8
J'ai fait une horloge qui change ses couleurs en fonction de l'heure du jour, indiquant l'heure locale. Au fil du temps, il changera lentement de couleurs, en utilisant des couleurs plus lumineuses le jour et des couleurs plus sombres la nuit.
La fenêtre est redimensionnable et l'horloge se redimensionne automatiquement à la taille que vous choisissez.
De plus, si l'utilisateur ajuste l'horloge du système ou si un changement d'heure d'été se produit, l'horloge le reflétera automatiquement.
Il existe deux formes pour l'exécuter:
Exécution du ClockDemo
fichier, c.-à-d java clock.ClockDemo
. Cela ouvrira une fenêtre et vous y verrez l'horloge.
Exécution du ClockSave
fichier, c.-à-d java clock.ClockSave filename width height [HH:mm:ss]
. Cela enregistrera simplement l'horloge dans un fichier PNG avec le nom de fichier, la largeur et la hauteur donnés. L'horloge sera tirée avec l'heure donnée ou, si elle est omise, avec l'heure actuelle. Par exemple, si vous l'exécutez car java clock.ClockSave clock.png 600 500 12:38:24
cela enregistrera l'horloge dans une image 600x500 dans un clock.png
fichier et l'horloge affichera 00h38:24. Utilisez les heures dans l'intervalle 00-23.
Captures d'écran
Voici quelques captures d'écran et fichiers générés:
00:36:50:
02:38:51:
06:42:13 AM:
11:15:28:
17h02: 37:
19h11h30:
21h29: 34:
Code source
J'ai séparé la source dans cinq fichiers différents dans un package appelé clock
.
Également disponible sur GitHub .
ClockDemo.java
package clock;
import java.awt.EventQueue;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
public class ClockDemo {
public static void main(String[] args) {
EventQueue.invokeLater(ClockDemo::runIt);
}
private static void runIt() {
final JFrame j = new JFrame();
j.setTitle("JClock");
final JClock clock = new JClock(new CoolPaint());
j.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
clock.stop();
j.dispose();
}
});
j.add(clock);
j.setBounds(20, 20, 600, 500);
j.setVisible(true);
clock.start();
}
}
ClockSave.java
package clock;
import java.io.IOException;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
public class ClockSave {
public static void main(String[] args) {
// Too much arguments.
if (args.length < 3 || args.length > 4) {
System.out.println("Bad usage: Should be java clock.ClockSave filename width height [HH:mm:ss]");
return;
}
// Parse the image size.
int h, w;
try {
w = Integer.parseInt(args[1]);
h = Integer.parseInt(args[2]);
} catch (NumberFormatException e) {
System.out.println("Bad usage: Should be java clock.ClockSave filename width height [HH:mm:ss]");
return;
}
// Parse the intended time.
LocalTime time;
if (args.length == 4) {
try {
DateTimeFormatter df = DateTimeFormatter.ofPattern("HH:mm:ss");
time = LocalTime.parse(args[3], df);
} catch (DateTimeParseException e) {
System.out.println("Bad usage: Should be java clock.ClockSave filename width height [HH:mm:ss]");
return;
}
} else {
time = LocalTime.now();
}
// Save to an image.
try {
new CoolPaint().saveClock(w, h, time, args[0]);
} catch (IOException e) {
System.out.println("Error on image output: " + e.getMessage());
}
}
}
JClock.java
package clock;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.time.LocalTime;
import java.time.temporal.ChronoField;
import javax.swing.JComponent;
public class JClock extends JComponent {
private static final long serialVersionUID = 1L;
private final CoolPaint paint;
private final Object lock;
private Thread updater;
public JClock(CoolPaint paint) {
this.paint = paint;
this.lock = new Object();
}
private void runClock() {
int lastTime = -1;
try {
while (isRunning()) {
Thread.sleep(10);
int t = time();
if (t != lastTime) {
lastTime = t;
repaint();
}
}
} catch (InterruptedException e) {
// Do nothing, the thread will die naturally.
}
}
private int time() {
return LocalTime.now().get(ChronoField.SECOND_OF_DAY);
}
private boolean isRunning() {
synchronized (lock) {
return updater == Thread.currentThread();
}
}
public void start() {
synchronized (lock) {
if (updater != null) return;
updater = new Thread(this::runClock);
updater.start();
}
}
public void stop() {
synchronized (lock) {
updater = null;
}
}
@Override
public void paintComponent(Graphics g) {
paint.paintClock(getWidth(), getHeight(), time(), (Graphics2D) g);
}
}
ClockPaint.java
package clock;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.time.LocalTime;
import java.time.temporal.ChronoField;
import javax.imageio.ImageIO;
public interface ClockPaint {
public void paintClock(int width, int height, int seconds, Graphics2D g2);
public default void paintClock(int width, int height, LocalTime time, Graphics2D g2) {
paintClock(width, height, time.get(ChronoField.SECOND_OF_DAY), g2);
}
public default void paintClock(int width, int height, Graphics2D g2) {
paintClock(width, height, LocalTime.now(), g2);
}
public default void saveClock(int width, int height, String fileName) throws IOException {
saveClock(width, height, LocalTime.now(), fileName);
}
public default void saveClock(int width, int height, LocalTime time, String fileName) throws IOException {
saveClock(width, height, time.get(ChronoField.SECOND_OF_DAY), fileName);
}
public default void saveClock(int width, int height, int seconds, String fileName) throws IOException {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
paintClock(width, height, seconds, (Graphics2D) image.getGraphics());
String f = fileName.endsWith(".png") ? fileName : fileName + ".png";
ImageIO.write(image, "png", new File(f));
}
}
CoolPaint.java
package clock;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.RadialGradientPaint;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
public class CoolPaint implements ClockPaint {
private static final int SECONDS_IN_MINUTE = 60;
private static final int SECONDS_IN_HALF_HOUR = 30 * SECONDS_IN_MINUTE;
private static final int SECONDS_IN_HOUR = 60 * SECONDS_IN_MINUTE;
private static final int SECONDS_IN_12_HOURS = 12 * SECONDS_IN_HOUR;
private static final int AM_0_00 = 0;
private static final int AM_3_00 = 3 * SECONDS_IN_HOUR;
private static final int AM_4_30 = 4 * SECONDS_IN_HOUR + SECONDS_IN_HALF_HOUR;
private static final int AM_7_30 = 7 * SECONDS_IN_HOUR + SECONDS_IN_HALF_HOUR;
private static final int AM_12_00 = 12 * SECONDS_IN_HOUR;
private static final int PM_4_30 = 16 * SECONDS_IN_HOUR + SECONDS_IN_HALF_HOUR;
private static final int PM_7_30 = 19 * SECONDS_IN_HOUR + SECONDS_IN_HALF_HOUR;
private static final int PM_9_00 = 21 * SECONDS_IN_HOUR;
private static final int PM_12_00 = 24 * SECONDS_IN_HOUR;
private static final Color BLACK = new Color(0, 0, 0);
private static final Color DARK_GRAY = new Color(32, 32, 32);
private static final Color DARK_BLUE = new Color(0, 0, 128);
private static final Color PURPLE = new Color(128, 0, 128);
private static final Color CYAN = new Color(0, 255, 255);
private static final Color YELLOW = new Color(225, 225, 0);
private static final Color PALE_YELLOW = new Color(224, 224, 64);
private static final Color RED = new Color(255, 0, 0);
private static final Color GREEN = new Color(0, 255, 0);
private static final Color LIGHT_BLUE = new Color(128, 128, 255);
private static final Color SKY_CYAN = new Color(48, 224, 224);
private static final Color[] COLOR_CYCLE = {
DARK_GRAY, LIGHT_BLUE, RED, PALE_YELLOW, GREEN, SKY_CYAN, LIGHT_BLUE, DARK_GRAY
};
private static final int RADIAL_PERIOD_LENGTH = PM_12_00 / COLOR_CYCLE.length;
private static final String[] ROMAN = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII"};
private static class Painter {
private final int width;
private final int height;
private final int seconds;
private final int radius;
private final Graphics2D g2;
private final int cx;
private final int cy;
private final int secondColorIndex;
private final int secondsInPeriod;
private final Color pointersAndNumbersColor;
public Painter(int width, int height, int seconds, Graphics2D g2) {
this.width = width;
this.height = height;
this.seconds = seconds;
this.radius = Math.min(width / 2, height / 2);
this.cx = width / 2;
this.cy = height / 2;
this.g2 = g2;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
this.secondColorIndex = seconds / RADIAL_PERIOD_LENGTH;
this.secondsInPeriod = seconds % RADIAL_PERIOD_LENGTH;
int startIndex = (secondColorIndex + COLOR_CYCLE.length + 5) % COLOR_CYCLE.length;
int endIndex = (secondColorIndex + COLOR_CYCLE.length + 6) % COLOR_CYCLE.length;
Color color1 = COLOR_CYCLE[startIndex];
Color color2 = COLOR_CYCLE[endIndex];
this.pointersAndNumbersColor = mixColors(color1, color2, 0, RADIAL_PERIOD_LENGTH, secondsInPeriod);
}
private int mixColorComponent(int startComponent, int endComponent, double position) {
int difference = endComponent - startComponent;
return startComponent + (int) (difference * position);
}
private Color mixColors(Color startColor, Color endColor, int startTime, int endTime, int currentTime) {
double normalized = (currentTime - startTime) / (double) (endTime - startTime);
return new Color(
mixColorComponent(startColor.getRed(), endColor.getRed(), normalized),
mixColorComponent(startColor.getGreen(), endColor.getGreen(), normalized),
mixColorComponent(startColor.getBlue(), endColor.getBlue(), normalized));
}
private Color upperBackgroundColor() {
if (seconds < 0) throw new IllegalArgumentException();
if (seconds <= AM_3_00) return BLACK;
if (seconds <= AM_4_30) return mixColors(BLACK, DARK_BLUE, AM_3_00, AM_4_30, seconds);
if (seconds <= AM_7_30) return mixColors(DARK_BLUE, CYAN, AM_4_30, AM_7_30, seconds);
if (seconds <= AM_12_00) return CYAN;
if (seconds <= PM_4_30) return CYAN;
if (seconds <= PM_7_30) return mixColors(CYAN, DARK_BLUE, PM_4_30, PM_7_30, seconds);
if (seconds <= PM_9_00) return mixColors(DARK_BLUE, BLACK, PM_7_30, PM_9_00, seconds);
if (seconds <= PM_12_00) return BLACK;
throw new IllegalArgumentException();
}
private Color lowerBackgroundColor() {
if (seconds < 0) throw new IllegalArgumentException();
if (seconds <= AM_3_00) return mixColors(BLACK, DARK_BLUE, AM_0_00, AM_3_00, seconds);
if (seconds <= AM_4_30) return mixColors(DARK_BLUE, PURPLE, AM_3_00, AM_4_30, seconds);
if (seconds <= AM_7_30) return mixColors(PURPLE, YELLOW, AM_4_30, AM_7_30, seconds);
if (seconds <= AM_12_00) return mixColors(YELLOW, CYAN, AM_7_30, AM_12_00, seconds);
if (seconds <= PM_4_30) return mixColors(CYAN, YELLOW, AM_12_00, PM_4_30, seconds);
if (seconds <= PM_7_30) return mixColors(YELLOW, PURPLE, PM_4_30, PM_7_30, seconds);
if (seconds <= PM_9_00) return mixColors(PURPLE, DARK_BLUE, PM_7_30, PM_9_00, seconds);
if (seconds <= PM_12_00) return mixColors(DARK_BLUE, BLACK, PM_9_00, PM_12_00, seconds);
throw new IllegalArgumentException();
}
private void paintBackground() {
Point2D p1 = new Point2D.Double(width / 2, 0);
Point2D p2 = new Point2D.Double(width / 2, height);
g2.setPaint(new GradientPaint(p1, upperBackgroundColor(), p2, lowerBackgroundColor()));
g2.fillRect(0, 0, width, height);
}
private RadialGradientPaint colorOnCycle(Point2D center, float radius) {
Color baseColor1 = COLOR_CYCLE[(secondColorIndex + COLOR_CYCLE.length - 1) % COLOR_CYCLE.length];
Color baseColor2 = COLOR_CYCLE[secondColorIndex];
Color baseColor3 = COLOR_CYCLE[(secondColorIndex + COLOR_CYCLE.length + 1) % COLOR_CYCLE.length];
Color baseColor4 = COLOR_CYCLE[(secondColorIndex + COLOR_CYCLE.length + 2) % COLOR_CYCLE.length];
Color start = mixColors(baseColor1, baseColor2, 0, RADIAL_PERIOD_LENGTH, secondsInPeriod);
Color end = mixColors(baseColor3, baseColor4, 0, RADIAL_PERIOD_LENGTH, secondsInPeriod);
float index2 = (RADIAL_PERIOD_LENGTH - secondsInPeriod) / (float) RADIAL_PERIOD_LENGTH / 2;
float index3 = 0.5f + index2;
float[] positions = index3 == 1.0 ? new float[] {0.0f, index2, 1.0f}
: new float[] {0.0f, index2, index3, 1.0f};
Color[] colors = index3 == 1.0 ? new Color[] {start, baseColor2, end}
: new Color[] {start, baseColor2, baseColor3, end};
return new RadialGradientPaint(center, radius, positions, colors);
}
private void paintClockArea() {
Point2D center = new Point2D.Double(width / 2, height / 2);
g2.setPaint(colorOnCycle(center, radius));
g2.fillOval(width / 2 - radius, height / 2 - radius, radius * 2, radius * 2);
}
private double pointerRevolutionsToRadians(double angle) {
return Math.toRadians((450 + angle * -360) % 360.0);
}
private void paintPointers() {
double hAngle = pointerRevolutionsToRadians(seconds % SECONDS_IN_12_HOURS / (double) SECONDS_IN_12_HOURS);
double mAngle = pointerRevolutionsToRadians(seconds % SECONDS_IN_HOUR / (double) SECONDS_IN_HOUR);
double sAngle = pointerRevolutionsToRadians(seconds % SECONDS_IN_MINUTE / (double) SECONDS_IN_MINUTE);
g2.setStroke(new BasicStroke(4.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2.drawLine(cx, cy, (int) (cx + Math.cos(hAngle) * radius * 0.55), (int) (cy - Math.sin(hAngle) * radius * 0.55));
g2.drawLine(cx, cy, (int) (cx + Math.cos(mAngle) * radius * 0.85), (int) (cy - Math.sin(mAngle) * radius * 0.85));
g2.setStroke(new BasicStroke(1.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2.drawLine(cx, cy, (int) (cx + Math.cos(sAngle) * radius * 0.85), (int) (cy - Math.sin(sAngle) * radius * 0.85));
}
private void paintNumbers() {
Font originalFont = g2.getFont();
double amplification = (int) Math.max(radius * 0.08, originalFont.getSize()) / (double) originalFont.getSize();
AffineTransform at0 = AffineTransform.getScaleInstance(amplification, amplification);
Font amplifiedFont = originalFont.deriveFont(at0);
g2.setFont(amplifiedFont);
FontMetrics fm = g2.getFontMetrics();
for (int i = 1; i <= 12; i++) {
double angle = pointerRevolutionsToRadians(i / 12.0);
double textInclination = Math.toRadians(30 * i);
AffineTransform at = AffineTransform.getRotateInstance(textInclination);
at.scale(amplification, amplification);
Font derivedFont = originalFont.deriveFont(at);
g2.setFont(derivedFont);
int pixelsOffset = fm.stringWidth(ROMAN[i]) / 2;
int xPlot = (int) (cx + Math.cos(angle) * radius * 0.9 - pixelsOffset * Math.cos(textInclination));
int yPlot = (int) (cy - Math.sin(angle) * radius * 0.9 - pixelsOffset * Math.sin(textInclination));
g2.drawString(ROMAN[i], xPlot, yPlot);
}
g2.setFont(originalFont);
}
private void paintDots() {
for (int i = 1; i < 60; i++) {
if (i % 5 == 0) continue;
double angle = pointerRevolutionsToRadians(i / 60.0);
g2.fillRect((int) (cx + Math.cos(angle) * radius * 0.9) - 1, (int) (cy - Math.sin(angle) * radius * 0.9) - 1, 3, 3);
}
}
public void paintClock() {
paintBackground();
paintClockArea();
g2.setColor(pointersAndNumbersColor);
g2.setPaint(pointersAndNumbersColor);
paintNumbers();
paintDots();
paintPointers();
}
}
@Override
public void paintClock(int width, int height, int seconds, Graphics2D g2) {
new Painter(width, height, seconds, g2).paintClock();
}
}
xclock
. Nécessite xclock pour être installé.