在 Java Swing 中,没有像 HTML5 <input type="date"> 那样功能单一、开箱即用的原生日期选择器控件,Swing 提供了非常灵活和强大的基础组件,我们可以通过组合它们来创建功能丰富的日期控件,Java 也提供了官方的 JXDatePicker,它属于 Swing 的一个扩展库。

下面我将从三个方面详细介绍 Java Swing 的日期控件:
- 手动组合基础组件(最灵活,但代码量稍多)
- 使用第三方库
JXDatePicker(最推荐,简单易用) - 使用 Java 8+ 的
java.timeAPI(现代、标准的日期时间处理)
手动组合基础组件 (JTextField + JButton + JDialog)
这是最经典、最灵活的方法,我们可以创建一个包含一个文本框和一个按钮的面板,点击按钮时,弹出一个模态对话框,对话框内部包含一个日历,用户选择日期后,日期会自动填充到文本框中,并关闭对话框。
创建一个自定义的 DatePicker 组件
我们将把这个逻辑封装到一个可重用的 JPanel 中。
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class CustomDatePicker extends JPanel {
private final JTextField textField;
private final JButton button;
private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
public CustomDatePicker() {
setLayout(new BorderLayout(5, 0));
// 文本框
textField = new JTextField(15);
textField.setEditable(false); // 用户不能直接输入,只能通过按钮选择
// 按钮
button = new JButton("选择日期");
button.addActionListener(e -> showDatePickerDialog());
this.add(textField, BorderLayout.CENTER);
this.add(button, BorderLayout.EAST);
}
private void showDatePickerDialog() {
// 获取当前日期作为日历的初始值
Calendar calendar = Calendar.getInstance();
Date initialDate = calendar.getTime();
// 创建日期选择器对话框
JDialog dialog = new JDialog((Frame) SwingUtilities.getWindowAncestor(this), "选择日期", true);
dialog.setLayout(new BorderLayout());
// 使用 JSpinner 来选择日期
// JSpinner 可以方便地处理日期的增加和减少
SpinnerDateModel model = new SpinnerDateModel(initialDate, null, null, Calendar.DAY_OF_MONTH);
JSpinner spinner = new JSpinner(model);
spinner.setEditor(new JSpinner.DateEditor(spinner, "yyyy-MM-dd"));
// 添加确定和取消按钮
JPanel buttonPanel = new JPanel();
JButton okButton = new JButton("确定");
JButton cancelButton = new JButton("取消");
okButton.addActionListener(e -> {
Date selectedDate = (Date) spinner.getValue();
textField.setText(dateFormat.format(selectedDate));
dialog.dispose();
});
cancelButton.addActionListener(e -> dialog.dispose());
buttonPanel.add(okButton);
buttonPanel.add(cancelButton);
dialog.add(spinner, BorderLayout.CENTER);
dialog.add(buttonPanel, BorderLayout.SOUTH);
dialog.pack();
dialog.setLocationRelativeTo(this); // 居中显示
dialog.setVisible(true);
}
// 获取用户选择的日期
public Date getSelectedDate() {
String dateStr = textField.getText();
try {
return dateFormat.parse(dateStr);
} catch (Exception e) {
return null; // 或者返回当前日期/空
}
}
// 获取日期的字符串表示
public String getSelectedDateAsString() {
return textField.getText();
}
}
如何使用这个自定义组件
import javax.swing.*;
import java.awt.*;
public class MainApplication {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("自定义日期选择器示例");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 200);
frame.setLayout(new FlowLayout());
// 创建并添加我们的自定义日期选择器
CustomDatePicker datePicker = new CustomDatePicker();
frame.add(datePicker);
// 添加一个按钮来获取选中的日期
JButton getButton = new JButton("获取日期");
getButton.addActionListener(e -> {
Date selectedDate = datePicker.getSelectedDate();
if (selectedDate != null) {
JOptionPane.showMessageDialog(frame, "您选择的日期是: " + selectedDate);
} else {
JOptionPane.showMessageDialog(frame, "您还没有选择日期!");
}
});
frame.add(getButton);
frame.setLocationRelativeTo(null); // 居中屏幕
frame.setVisible(true);
});
}
}
优点:

- 完全控制UI的外观和行为。
- 不需要引入任何外部库。
- 学习Swing组件组合的好例子。
缺点:
- 代码量较大,需要自己处理布局和事件逻辑。
- 实现一个功能完善的日历视图(类似月历)会非常复杂。
使用第三方库 JXDatePicker (强烈推荐)
JXDatePicker 是 SwingX 项目的一部分,SwingX 是一个对 Swing 的扩展库,提供了许多现代、美观且功能强大的组件,包括一个非常好用的日期选择器。
添加依赖
如果你使用 Maven,在 pom.xml 中添加:
<dependency>
<groupId>org.swinglabs</groupId>
<artifactId>swingx</artifactId>
<version>1.6.1</version> <!-- 使用最新的稳定版本 -->
</dependency>
如果你使用 Gradle,在 build.gradle 中添加:
implementation 'org.swinglabs:swingx:1.6.1'
使用 JXDatePicker
JXDatePicker 的使用非常简单,几乎和原生组件一样。
import org.jdesktop.swingx.JXDatePicker;
import javax.swing.*;
import java.awt.*;
import java.util.Date;
public class JXDatePickerExample {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("JXDatePicker 示例");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 200);
frame.setLayout(new FlowLayout());
// 1. 直接创建 JXDatePicker
JXDatePicker datePicker = new JXDatePicker();
// 设置默认选中今天的日期
datePicker.setDate(new Date());
// 2. 可以设置日期格式
datePicker.setFormats("yyyy年MM月dd日", "yyyy-MM-dd");
// 3. 添加一个监听器,当日期改变时触发
datePicker.addActionListener(e -> {
Date selectedDate = datePicker.getDate();
System.out.println("选择的日期是: " + selectedDate);
JOptionPane.showMessageDialog(frame, "您选择的日期是: " + selectedDate);
});
frame.add(new JLabel("请选择日期:"));
frame.add(datePicker);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
优点:
- 开箱即用:功能非常完整,包括弹出式月历、日期格式化等。
- 外观现代:比手动组合的组件看起来更专业。
- 易于使用:API 设计直观,学习成本低。
- 可扩展性强:可以轻松地与
JXMonthView等其他 SwingX 组件配合。
缺点:
- 需要引入第三方依赖。
结合 Java 8+ 的 java.time API (最佳实践)
无论你选择方案一还是方案二,处理日期数据时,强烈建议使用 Java 8 引入的 java.time 包,而不是过时的 java.util.Date 和 java.util.Calendar。java.time 更安全、更易用。
修改方案一的 CustomDatePicker 以使用 java.time
我们只需要修改 getSelectedDate() 方法,使其返回 LocalDate 类型。
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
// ... 在 CustomDatePicker 类中 ...
private final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
// ... showDatePickerDialog 方法中 ...
// JSpinner 的模型需要调整,或者直接使用 JCalendar 等更复杂的库。
// 为了简单起见,这里我们假设 JSpinner 仍然返回 Date 对象。
// 然后将其转换为 LocalDate。
okButton.addActionListener(e -> {
Date selectedDate = (Date) spinner.getValue();
// 将 java.util.Date 转换为 java.time.LocalDate
Instant instant = selectedDate.toInstant();
ZoneId zoneId = ZoneId.systemDefault(); // 获取系统默认时区
LocalDate localDate = instant.atZone(zoneId).toLocalDate();
textField.setText(dateFormatter.format(localDate));
dialog.dispose();
});
// 新增方法,用于获取 java.time.LocalDate
public LocalDate getSelectedLocalDate() {
String dateStr = textField.getText();
try {
return LocalDate.parse(dateStr, dateFormatter);
} catch (DateTimeParseException e) {
return null; // 或者返回 LocalDate.now()
}
}
在主程序中,你就可以得到 LocalDate 对象,它提供了更丰富的方法,如 plusDays(), getDayOfWeek() 等。
// 在 MainApplication 的 ActionListener 中
LocalDate selectedLocalDate = datePicker.getSelectedLocalDate();
if (selectedLocalDate != null) {
JOptionPane.showMessageDialog(frame, "您选择的日期是: " + selectedLocalDate + ", 是周" + selectedLocalDate.getDayOfWeek().getValue());
}
为什么推荐 java.time?
- 不可变:所有
java.time类都是不可变的,线程安全,避免了Date和Calendar的可变性问题。 - API 清晰:方法命名清晰直观,如
plusDays,minusMonths。 - 时区处理:提供了
ZonedDateTime和ZoneId来完美处理时区问题。 - 现代标准:是 Java 官方推荐的日期和时间API。
总结与选择建议
| 特性 | 手动组合 (JTextField + JButton) |
JXDatePicker (SwingX) |
|---|---|---|
| 易用性 | 低,需要编写大量代码 | 高,开箱即用 |
| 功能完整性 | 低(基础),高(需大量开发) | 高(自带月历、格式化等) |
| 外观 | 依赖基础组件,需自行美化 | 现代、专业 |
| 依赖 | 无 | 需要 swingx 库 |
| 灵活性 | 极高,完全掌控 | 较高,可通过配置满足大部分需求 |
与 java.time 结合 |
容易,只需修改返回类型 | 容易,只需转换 Date 对象 |
如何选择?
- 对于绝大多数项目:强烈推荐使用
JXDatePicker,它能以最小的代码量提供最好的用户体验,是工业界的标准做法。 - 对于学习或需要极致定制化:可以尝试手动组合组件,这有助于你深入理解 Swing 的工作原理,适合开发一些非常特殊、UI 要求极高的内部工具。
- 无论哪种UI组件:在后台处理和存储日期数据时,请务必使用 Java 8+ 的
java.timeAPI,这是现代 Java 开发的最佳实践。
