引言
本示例适合 Java 初学者,同时也适合有需求的朋友直接使用程序(直接下载资源绑定的 Jar 文件运行即可,但需要自行配置 JDK)。
开发目标
- 全屏显示
- 软件启动后自动进入全屏模式,占据整个屏幕,无菜单栏、任务栏干扰。
- 支持多种屏幕分辨率,自适应不同尺寸的显示器。
- 时钟样式
- 提供至少三种不同的时钟样式供用户选择,如数字时钟、指针时钟、简约时钟等。
- 用户可以随时切换时钟样(自定义文字大小、倒数日目标日期、设置背景或文字颜色样式等),满足不同的视觉需求。
- 时间显示
- 准确显示当前的年、月、日、星期、时、分、秒。
- 时间更新间隔不超过 1 秒,确保时间显示的准确性。且保持屏幕常亮。
最终效果
代码示例
import javax.swing.*;
import java.awt.*;
import java.awt.Robot;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Properties;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
// 主窗口类
public class FullScreenClock extends JFrame {
private JLabel clockLabel;
private JLabel daysLabel; // 用于显示距离目标日期的天数
private Timer clockTimer;
private Settings settings;
private JPopupMenu popupMenu;
// 目标日期(使用 Date 表示)
private Date targetDate;
private Timer f5Timer;
private Robot robot;
private boolean firstSpacePress = true;
private Timer spacePressTimer;
private JLabel countdownLabel; // 用于显示倒计时
private Timer countdownTimer;
private long countdownEndTime;
private boolean isCountdownRunning;
public FullScreenClock() {
settings = new Settings();
settings.loadSettings(); // 加载默认设置
// 确保 config.properties 文件存在
ensureConfigFileExists();
// 设置窗口为全屏
setExtendedState(JFrame.MAXIMIZED_BOTH);
setUndecorated(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 创建时钟和天数标签
clockLabel = new JLabel();
clockLabel.setFont(new Font(settings.getFontName(), Font.PLAIN, settings.getFontSize())); // 使用设置的字体大小和名称
clockLabel.setHorizontalAlignment(JLabel.CENTER);
clockLabel.setForeground(settings.getTextColor()); // 使用设置的文字颜色
daysLabel = new JLabel();
daysLabel.setFont(new Font(settings.getFontName(), Font.PLAIN, settings.getFontSize() / 4)); // 天数字体大小为时间字体的1/4
daysLabel.setHorizontalAlignment(JLabel.CENTER);
daysLabel.setForeground(settings.getTextColor()); // 使用设置的文字颜色
// 更新天数
updateDaysUntilTargetDate();
// 创建面板并设置布局
JPanel panel = new JPanel();
panel.setLayout(new GridBagLayout()); // 使用 GridBagLayout
panel.setBackground(settings.getBackgroundColor()); // 使用设置的背景颜色
// 添加天数和时间标签到面板,确保紧密排列
GridBagConstraints gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.HORIZONTAL; // 水平填充
gbc.weightx = 1.0; // 水平权重
gbc.insets = new Insets(0, 0, 0, 0); // 移除内边距
gbc.gridy = 0; // 天数标签在上
gbc.weighty = 0.1; // 占用较少垂直空间
panel.add(daysLabel, gbc);
gbc.gridy = 1; // 时间标签在下
gbc.weighty = 0.9; // 占用较多垂直空间
panel.add(clockLabel, gbc);
countdownLabel = new JLabel();
countdownLabel.setFont(new Font(settings.getFontName(), Font.PLAIN, settings.getFontSize() / 4)); // 倒计时字体大小为时间字体的1/4
countdownLabel.setHorizontalAlignment(JLabel.CENTER);
countdownLabel.setForeground(settings.getTextColor()); // 使用设置的文字颜色
countdownLabel.setVisible(false); // 初始状态下不可见
gbc.gridy = 2; // 倒计时标签在下
gbc.weighty = 0.1; // 占用较少垂直空间
panel.add(countdownLabel, gbc);
add(panel, BorderLayout.CENTER);
// 每秒更新一次时钟和天数
clockTimer = new Timer();
clockTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
updateClock();
updateMode();
updateDaysUntilTargetDate();
}
}, 0, 1000);
// 右键菜单
createPopupMenu();
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
if (e.isPopupTrigger()) {
popupMenu.show(e.getComponent(), e.getX(), e.getY());
}
}
@Override
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger()) {
popupMenu.show(e.getComponent(), e.getX(), e.getY());
}
}
});
loadTargetDate();
if (targetDate == null) {
targetDate = new Date(2025 - 1900, 11, 20); // 默认日期
}
try {
robot = new Robot();
} catch (AWTException e) {
e.printStackTrace();
}
// 启动F5定时器
startF5Timer();
// 确保字体和天数的显示状态在程序一开始运行的时候能够正确加载
updateClock();
updateDaysUntilTargetDate();
updateMode();
}
private void updateClock() {
SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
String time = dateFormat.format(new Date());
clockLabel.setText(time);
}
private void updateDaysUntilTargetDate() {
// 检查 targetDate 是否为 null
if (targetDate == null) {
daysLabel.setText("Target date not set");
return;
}
long now = System.currentTimeMillis(); // 当前时间的毫秒数
long diffInMillis = targetDate.getTime() - now; // 目标日期与当前时间的差值(毫秒)
// 如果目标日期已经过去,则设置天数为0
if (diffInMillis < 0) {
daysLabel.setText("Target date has passed");
return;
}
long days = diffInMillis / (1000L * 60 * 60 * 24); // 计算剩余天数
daysLabel.setText("Only " + days + " Days");
}
private void updateMode() {
if (settings.isAutoMode()) {
Date now = new Date();
long currentTime = now.getTime();
long startOfDay = now.getTime() - now.getHours() * 3600000 - now.getMinutes() * 60000 - now.getSeconds() * 1000;
long endOfDay = startOfDay + 24 * 3600000;
long morning = startOfDay + 6 * 3600000; // 6:00 AM
long evening = startOfDay + 18 * 3600000; // 6:00 PM
boolean isDaytime = currentTime >= morning && currentTime < evening;
JPanel panel = (JPanel) getContentPane().getComponent(0); // 获取主面板
if (isDaytime) {
if (!settings.getMode().equals("day")) {
settings.setMode("day");
// 更新背景和文字颜色
panel.setBackground(Color.WHITE); // 白天背景
clockLabel.setForeground(Color.BLACK);
daysLabel.setForeground(Color.BLACK);
}
} else {
if (!settings.getMode().equals("night")) {
settings.setMode("night");
// 更新背景和文字颜色
panel.setBackground(Color.BLACK); // 夜晚背景
clockLabel.setForeground(Color.WHITE);
daysLabel.setForeground(Color.WHITE);
}
}
settings.saveSettings(); // 保存设置
}
}
// 更新右键菜单
private void createPopupMenu() {
popupMenu = new JPopupMenu();
// 设置时间文字大小
JMenuItem fontSizeItem = new JMenuItem("设置时间文字大小");
fontSizeItem.addActionListener(e -> {
String input = JOptionPane.showInputDialog(this, "请输入时间文字大小:");
if (input != null) {
try {
int fontSize = Integer.parseInt(input);
clockLabel.setFont(new Font(clockLabel.getFont().getFontName(), Font.PLAIN, fontSize));
daysLabel.setFont(new Font(clockLabel.getFont().getFontName(), Font.PLAIN, fontSize / 4)); // 天数字体大小为时间字体的1/4
settings.setFontSize(fontSize); // 保存字体大小设置
settings.saveSettings(); // 保存设置
} catch (NumberFormatException ex) {
JOptionPane.showMessageDialog(this, "输入的不是有效的数字", "错误", JOptionPane.ERROR_MESSAGE);
}
}
});
popupMenu.add(fontSizeItem);
// 修改文字字体
JMenuItem changeFontItem = new JMenuItem("修改文字字体");
changeFontItem.addActionListener(e -> {
// 获取系统中可用的所有字体名称
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
String[] fontNames = ge.getAvailableFontFamilyNames();
// 创建一个 JComboBox 并添加字体名称
JComboBox<String> fontComboBox = new JComboBox<>(fontNames);
fontComboBox.setEditable(true); // 允许手动输入
// 使用 JOptionPane 显示 JComboBox
int result = JOptionPane.showConfirmDialog(this, fontComboBox, "选择字体", JOptionPane.OK_CANCEL_OPTION);
if (result == JOptionPane.OK_OPTION) {
String selectedFontName = (String) fontComboBox.getSelectedItem();
if (selectedFontName != null && !selectedFontName.trim().isEmpty()) {
try {
Font newFont = new Font(selectedFontName, Font.PLAIN, clockLabel.getFont().getSize());
clockLabel.setFont(newFont);
daysLabel.setFont(newFont.deriveFont(clockLabel.getFont().getSize() / 4f)); // 天数字体大小为时间字体的1/4
settings.setFontName(selectedFontName); // 保存字体名称设置
settings.saveSettings(); // 保存设置
} catch (Exception ex) {
JOptionPane.showMessageDialog(this, "输入的字体名称无效", "错误", JOptionPane.ERROR_MESSAGE);
}
}
}
});
popupMenu.add(changeFontItem);
// 设置背景颜色
JMenuItem backgroundColorItem = new JMenuItem("设置背景颜色");
backgroundColorItem.addActionListener(e -> {
Color color = JColorChooser.showDialog(this, "选择背景颜色", settings.getBackgroundColor());
if (color != null) {
JPanel panel = (JPanel) getContentPane().getComponent(0); // 获取主面板
panel.setBackground(color);
clockLabel.setForeground(settings.getTextColor());
daysLabel.setForeground(settings.getTextColor());
settings.setBackgroundColor(color); // 保存背景颜色设置
settings.saveSettings(); // 保存设置
}
});
popupMenu.add(backgroundColorItem);
// 设置文字颜色
JMenuItem textColorItem = new JMenuItem("设置文字颜色");
textColorItem.addActionListener(e -> {
Color color = JColorChooser.showDialog(this, "选择文字颜色", settings.getTextColor());
if (color != null) {
clockLabel.setForeground(color);
daysLabel.setForeground(color);
settings.setTextColor(color); // 保存文字颜色设置
settings.saveSettings(); // 保存设置
}
});
popupMenu.add(textColorItem);
// 切换黑白模式
JMenuItem toggleModeItem = new JMenuItem("切换黑白模式");
toggleModeItem.addActionListener(e -> {
settings.setAutoMode(false);
JPanel panel = (JPanel) getContentPane().getComponent(0); // 获取主面板
if (settings.getMode().equals("day")) {
settings.setMode("night");
panel.setBackground(Color.BLACK);
clockLabel.setForeground(Color.WHITE);
daysLabel.setForeground(Color.WHITE);
} else {
settings.setMode("day");
panel.setBackground(Color.WHITE);
clockLabel.setForeground(Color.BLACK);
daysLabel.setForeground(Color.BLACK);
}
settings.saveSettings(); // 保存设置
});
popupMenu.add(toggleModeItem);
// 显示/隐藏倒计天数
JMenuItem toggleDaysItem = new JMenuItem("显示/隐藏倒计天数");
toggleDaysItem.addActionListener(e -> {
daysLabel.setVisible(!daysLabel.isVisible());
settings.setShowDays(daysLabel.isVisible()); // 保存显示/隐藏倒计天数的设置
settings.saveSettings(); // 保存设置
});
popupMenu.add(toggleDaysItem);
// 修改目标日期
JMenuItem setTargetDateItem = new JMenuItem("设置目标日期");
setTargetDateItem.addActionListener(e -> {
String input = JOptionPane.showInputDialog(this, "请输入目标日期 (格式: yyyy-MM-dd):");
if (input != null) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
try {
Date newTargetDate = dateFormat.parse(input);
setTargetDate(newTargetDate);
} catch (Exception ex) {
JOptionPane.showMessageDialog(this, "输入的日期格式不正确", "错误", JOptionPane.ERROR_MESSAGE);
}
}
});
popupMenu.add(setTargetDateItem);
// 倒计时功能
JMenuItem startCountdownItem = new JMenuItem("开始倒计时");
startCountdownItem.addActionListener(e -> {
String input = JOptionPane.showInputDialog(this, "请输入倒计时时间 (格式: HH:mm:ss):");
if (input != null) {
try {
String[] parts = input.split(":");
int hours = Integer.parseInt(parts[0]);
int minutes = Integer.parseInt(parts[1]);
int seconds = Integer.parseInt(parts[2]);
long countdownTime = (hours * 3600 + minutes * 60 + seconds) * 1000;
startCountdown(countdownTime);
} catch (NumberFormatException | ArrayIndexOutOfBoundsException ex) {
JOptionPane.showMessageDialog(this, "输入的格式不正确", "错误", JOptionPane.ERROR_MESSAGE);
}
}
});
popupMenu.add(startCountdownItem);
JMenuItem pauseCountdownItem = new JMenuItem("暂停倒计时");
pauseCountdownItem.addActionListener(e -> pauseCountdown());
popupMenu.add(pauseCountdownItem);
JMenuItem resetCountdownItem = new JMenuItem("重置倒计时");
resetCountdownItem.addActionListener(e -> resetCountdown());
popupMenu.add(resetCountdownItem);
// 退出
JMenuItem exitItem = new JMenuItem("退出");
exitItem.addActionListener(e -> System.exit(0));
popupMenu.add(exitItem);
}
public void setTargetDate(Date date) {
this.targetDate = date;
saveTargetDate();
}
private void saveTargetDate() {
Properties props = new Properties();
props.setProperty("targetDate", targetDate.getTime() + "");
try (FileOutputStream fos = new FileOutputStream("config.properties")) {
props.store(fos, "Target Date Configuration");
} catch (IOException e) {
e.printStackTrace();
}
}
private void loadTargetDate() {
Properties props = new Properties();
try (FileInputStream fis = new FileInputStream("config.properties")) {
props.load(fis);
String dateStr = props.getProperty("targetDate");
if (dateStr != null) {
targetDate = new Date(Long.parseLong(dateStr));
}
} catch (IOException e) {
// 文件不存在或其他IO错误,保持targetDate为null
}
}
private void startF5Timer() {
f5Timer = new Timer();
f5Timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
sendF5Key();
}
}, 0, 60000); // 每分钟发送一次F5键
}
private void sendF5Key() {
if (robot != null) {
if (firstSpacePress) {
firstSpacePress = false;
spacePressTimer = new Timer();
spacePressTimer.schedule(new TimerTask() {
@Override
public void run() {
robot.keyPress(KeyEvent.VK_SPACE);
robot.keyRelease(KeyEvent.VK_SPACE);
firstSpacePress = true;
}
}, 1000); // 20秒延迟
} else {
robot.keyPress(KeyEvent.VK_SPACE);
robot.keyRelease(KeyEvent.VK_SPACE);
}
}
}
@Override
public void dispose() {
// 停止F5定时器
if (f5Timer != null) {
f5Timer.cancel();
}
// 停止spacePressTimer
if (spacePressTimer != null) {
spacePressTimer.cancel();
}
super.dispose();
}
private void ensureConfigFileExists() {
File configFile = new File("config.properties");
if (!configFile.exists()) {
try {
configFile.createNewFile();
// 初始化config.properties文件内容
Properties props = new Properties();
props.setProperty("targetDate", new Date().getTime() + ""); // 设置默认目标日期
try (FileOutputStream fos = new FileOutputStream(configFile)) {
props.store(fos, "Target Date Configuration");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
FullScreenClock clock = new FullScreenClock();
clock.setVisible(true);
});
}
private void startCountdown(long countdownTime) {
if (isCountdownRunning) {
return;
}
countdownEndTime = System.currentTimeMillis() + countdownTime;
isCountdownRunning = true;
countdownLabel.setVisible(true);
countdownTimer = new Timer();
countdownTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
updateCountdownLabel();
}
}, 0, 1000);
}
private void pauseCountdown() {
if (!isCountdownRunning) {
return;
}
isCountdownRunning = false;
countdownTimer.cancel();
}
private void resetCountdown() {
if (isCountdownRunning) {
pauseCountdown();
}
countdownLabel.setText("");
countdownLabel.setVisible(false);
}
private void updateCountdownLabel() {
long currentTime = System.currentTimeMillis();
long diffInMillis = countdownEndTime - currentTime;
if (diffInMillis <= 0) {
countdownLabel.setText("时间到!");
resetCountdown();
return;
}
long hours = diffInMillis / (1000L * 60 * 60);
long minutes = (diffInMillis % (1000L * 60 * 60)) / (1000L * 60);
long seconds = (diffInMillis % (1000L * 60)) / 1000;
countdownLabel.setText(String.format("%02d:%02d:%02d", hours, minutes, seconds));
}
}
// 设置类
class Settings {
private String mode;
private boolean autoMode;
private int fontSize;
private Color backgroundColor;
private Color textColor;
private boolean showDays;
private String fontName; // 新增字体名称属性
public Settings() {
// 设置默认为暗黑模式
mode = "night";
autoMode = true;
fontSize = 200; // 默认字体大小
backgroundColor = Color.BLACK; // 默认背景颜色
textColor = Color.WHITE; // 默认文字颜色
showDays = true; // 默认显示倒计天数
fontName = "Arial"; // 默认字体名称
}
public String getMode() {
return mode;
}
public void setMode(String mode) {
this.mode = mode;
}
public boolean isAutoMode() {
return autoMode;
}
public void setAutoMode(boolean autoMode) {
this.autoMode = autoMode;
}
public int getFontSize() {
return fontSize;
}
public void setFontSize(int fontSize) {
this.fontSize = fontSize;
}
public Color getBackgroundColor() {
return backgroundColor;
}
public void setBackgroundColor(Color backgroundColor) {
this.backgroundColor = backgroundColor;
}
public Color getTextColor() {
return textColor;
}
public void setTextColor(Color textColor) {
this.textColor = textColor;
}
public boolean isShowDays() {
return showDays;
}
public void setShowDays(boolean showDays) {
this.showDays = showDays;
}
public String getFontName() {
return fontName;
}
public void setFontName(String fontName) {
this.fontName = fontName;
}
public void loadSettings() {
Properties props = new Properties();
try (FileInputStream fis = new FileInputStream("config.properties")) {
props.load(fis);
mode = props.getProperty("mode", "night");
autoMode = Boolean.parseBoolean(props.getProperty("autoMode", "true"));
fontSize = Integer.parseInt(props.getProperty("fontSize", "200"));
// 确保颜色字符串以#开头
String bgColorStr = props.getProperty("backgroundColor", "000000");
if (!bgColorStr.startsWith("#")) {
bgColorStr = "#" + bgColorStr;
}
backgroundColor = Color.decode(bgColorStr);
String textColorStr = props.getProperty("textColor", "FFFFFF");
if (!textColorStr.startsWith("#")) {
textColorStr = "#" + textColorStr;
}
textColor = Color.decode(textColorStr);
showDays = Boolean.parseBoolean(props.getProperty("showDays", "true"));
fontName = props.getProperty("fontName", "Arial"); // 加载字体名称设置
} catch (IOException e) {
// 文件不存在或其他IO错误,保持默认设置
}
}
public void saveSettings() {
Properties props = new Properties();
props.setProperty("mode", mode);
props.setProperty("autoMode", String.valueOf(autoMode));
props.setProperty("fontSize", String.valueOf(fontSize));
props.setProperty("backgroundColor", "#" + Integer.toHexString(backgroundColor.getRGB()).substring(2)); // 确保颜色字符串以#开头
props.setProperty("textColor", "#" + Integer.toHexString(textColor.getRGB()).substring(2)); // 确保颜色字符串以#开头
props.setProperty("showDays", String.valueOf(showDays));
props.setProperty("fontName", fontName); // 保存字体名称设置
try (FileOutputStream fos = new FileOutputStream("config.properties")) {
props.store(fos, "Settings Configuration");
} catch (IOException e) {
e.printStackTrace();
}
}
}
标签:Java,settings,daysLabel,void,private,倒计时,全屏,JMenuItem,new
From: https://blog.csdn.net/m0_66817474/article/details/145814154