设计背景
在某一阶段可能需要给自己设置长期任务,比如找工作、考研等,需要一个单纯的任务计时工具,设置完任务的目标时间后,每次打开App时都能直接看到最新的剩余时间
设计步骤
1. 写java源码
由于需要界面显示,需要用到swing库等,以下是源码
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.List;
public class CountdownTimer extends JFrame {
private JTextField taskNameField;
private JTextField dateTimeField;
private JLabel messageLabel;
private static final String FILE_PATH = "tasks.txt";
private DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private List<Task> tasks = new ArrayList<>();
private JPanel tasksPanel;
public CountdownTimer() {
setTitle("倒计时器");
setSize(600, 400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
// 创建输入框和按钮
taskNameField = new JTextField(10);
dateTimeField = new JTextField(20);
messageLabel = new JLabel("输入任务名和目标时间 (yyyy-MM-dd HH:mm:ss)", SwingConstants.CENTER);
JButton addButton = new JButton("添加任务");
// 添加事件监听器
addButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
addTask();
}
});
// 布局
setLayout(new BorderLayout());
JPanel inputPanel = new JPanel();
inputPanel.add(new JLabel("任务名:"));
inputPanel.add(taskNameField);
inputPanel.add(new JLabel("目标时间:"));
inputPanel.add(dateTimeField);
inputPanel.add(addButton);
tasksPanel = new JPanel();
tasksPanel.setLayout(new BoxLayout(tasksPanel, BoxLayout.Y_AXIS));
add(inputPanel, BorderLayout.NORTH);
add(new JScrollPane(tasksPanel), BorderLayout.CENTER);
add(messageLabel, BorderLayout.SOUTH);
// 读取并启动倒计时
loadTasks();
}
private void addTask() {
String taskName = taskNameField.getText();
String targetDateTimeStr = dateTimeField.getText();
try {
LocalDateTime targetDateTime = LocalDateTime.parse(targetDateTimeStr, formatter);
Task task = new Task(taskName, targetDateTime);
tasks.add(task);
saveTasks();
displayTasks();
taskNameField.setText("");
dateTimeField.setText("");
messageLabel.setText("任务添加成功!");
} catch (Exception ex) {
messageLabel.setText("请输入正确的日期时间格式: yyyy-MM-dd HH:mm:ss");
}
}
private void displayTasks() {
tasksPanel.removeAll();
for (Task task : tasks) {
JPanel taskPanel = new JPanel(new BorderLayout());
JLabel taskLabel = new JLabel();
taskPanel.add(taskLabel, BorderLayout.CENTER);
JButton deleteButton = new JButton("删除");
deleteButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
tasks.remove(task);
saveTasks();
displayTasks();
}
});
taskPanel.add(deleteButton, BorderLayout.EAST);
tasksPanel.add(taskPanel);
startTimer(task, taskLabel);
}
tasksPanel.revalidate();
tasksPanel.repaint();
}
private void startTimer(Task task, JLabel taskLabel) {
java.util.Timer timer = new java.util.Timer();
timer.scheduleAtFixedRate(new java.util.TimerTask() {
@Override
public void run() {
LocalDateTime now = LocalDateTime.now();
long seconds = ChronoUnit.SECONDS.between(now, task.getTargetDateTime());
if (seconds <= 0) {
taskLabel.setText("<html>" + task.getName() + ": <font size='5'>时间到了!</font></html>");
timer.cancel();
} else {
long years = ChronoUnit.YEARS.between(now, task.getTargetDateTime());
now = now.plusYears(years);
long months = ChronoUnit.MONTHS.between(now, task.getTargetDateTime());
now = now.plusMonths(months);
long days = ChronoUnit.DAYS.between(now, task.getTargetDateTime());
now = now.plusDays(days);
long hours = ChronoUnit.HOURS.between(now, task.getTargetDateTime());
now = now.plusHours(hours);
long minutes = ChronoUnit.MINUTES.between(now, task.getTargetDateTime());
now = now.plusMinutes(minutes);
long secs = ChronoUnit.SECONDS.between(now, task.getTargetDateTime());
String timeText = String.format("%d年 %d月 %d天 %02d小时 %02d分 %02d秒",
years, months, days, hours, minutes, secs);
// Pad numbers with zeros to ensure stable layout
String paddedTimeText = timeText.replaceAll("\\b(\\d)\\b", "0$1");
// Format the time text with HTML tags to set different font sizes
String formattedText = "<html>" + task.getName() + ": ";
formattedText += "<font size='5'>" + paddedTimeText + "</font></html>";
taskLabel.setText(formattedText);
}
}
}, 0, 1000);
}
private void saveTasks() {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(FILE_PATH))) {
for (Task task : tasks) {
writer.write(task.getName() + ";" + task.getTargetDateTime().format(formatter));
writer.newLine();
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void loadTasks() {
tasks.clear();
try (BufferedReader reader = new BufferedReader(new FileReader(FILE_PATH))) {
String line;
while ((line = reader.readLine()) != null) {
String[] parts = line.split(";");
if (parts.length == 2) {
String taskName = parts[0];
LocalDateTime targetDateTime = LocalDateTime.parse(parts[1], formatter);
tasks.add(new Task(taskName, targetDateTime));
}
}
displayTasks();
} catch (IOException e) {
messageLabel.setText("尚未设置任务。");
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new CountdownTimer().setVisible(true);
}
});
}
}
class Task {
private String name;
private LocalDateTime targetDateTime;
public Task(String name, LocalDateTime targetDateTime) {
this.name = name;
this.targetDateTime = targetDateTime;
}
public String getName() {
return name;
}
public LocalDateTime getTargetDateTime() {
return targetDateTime;
}
}
2. 将java源码转为jar文件
JAR文件是一种软件包文件格式,通常用于聚合大量的Java类文件、相关的元数据和资源(文本、图片等)文件到一个文件,以便开发Java平台应用软件或库。
将源码转为jar文件具体可参考这篇博客:如何将.java文件转为.jar
3. 使用launch4j工具将jar文件转为exe可执行文件
具体步骤可参考这篇博客:luanch4j转exe
App展示
这是App的界面演示
App自取链接:https://pan.baidu.com/s/1C5Nc5ZsdFsSVU3-SEEsdVA
提取码:dddd
开机自启动
若是想开机直接能看到程序启动,
1.记好转好的jar文件地址
2. 用记事本写一个文件,内容为: java -jar “jar文件的地址” ,随后将文件后缀改为.bat
3. win+R打开运行窗口,输入shell:startup,打开开机自启动文件夹,将前面得到的.bat文件放入这个文件夹中即可