首页 > 编程语言 >【Java入门案例】全屏时钟+倒数日+倒计时(完整代码)

【Java入门案例】全屏时钟+倒数日+倒计时(完整代码)

时间:2025-02-23 19:53:28浏览次数:7  
标签:Java settings daysLabel void private 倒计时 全屏 JMenuItem new

引言

本示例适合 Java 初学者,同时也适合有需求的朋友直接使用程序(直接下载资源绑定的 Jar 文件运行即可,但需要自行配置 JDK)。

开发目标

  1. 全屏显示
    • 软件启动后自动进入全屏模式,占据整个屏幕,无菜单栏、任务栏干扰。
    • 支持多种屏幕分辨率,自适应不同尺寸的显示器。
  2. 时钟样式
    • 提供至少三种不同的时钟样式供用户选择,如数字时钟、指针时钟、简约时钟等。
    • 用户可以随时切换时钟样(自定义文字大小、倒数日目标日期、设置背景或文字颜色样式等),满足不同的视觉需求。
  3. 时间显示
    • 准确显示当前的年、月、日、星期、时、分、秒。
    • 时间更新间隔不超过 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

相关文章