上一篇
Java做窗口日历,可借助Swing库的JFrame搭建
窗口,结合Calendar类处理日期逻辑
基础框架搭建
-
导入依赖库
需引入Swing组件库与AWT布局管理器:import javax.swing.; // JFrame, JPanel等组件 import java.awt.; // 布局管理类如GridLayout import java.util.Calendar; // 日期计算核心类 import java.util.Date; // 处理当前时间戳
-
创建主窗口(JFrame)
继承JFrame并设置基本属性:public class MyCalendar extends JFrame { public MyCalendar() { setTitle("Java窗口日历"); // 窗口标题 setSize(400, 300); // 宽高尺寸 setDefaultCloseOperation(EXIT_ON_CLOSE); // 关闭事件响应 setLocationRelativeTo(null); // 屏幕居中显示 initializeUI(); // 初始化用户界面 } } -
添加主面板(JPanel)
使用双层嵌套布局实现结构化排版:外层采用BorderLayout划分区域,内层用GridLayout(7,7)构建表格形式的日期网格。private void initializeUI() { // 顶部标题栏 显示年月信息 JLabel titleLabel = new JLabel("", SwingConstants.CENTER); titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 16)); add(titleLabel, BorderLayout.NORTH); // 中间主体 星期与日期表格 JPanel gridPanel = new JPanel(new GridLayout(7, 7)); // ...后续填充单元格内容... add(gridPanel, BorderLayout.CENTER); }
动态生成日历内容
关键算法实现
通过Calendar类获取当前月份的数据结构:
// 初始化当月第一天对应的星期几(周日=1,周一=2...饱和六=7) int firstDayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); // 注意:Java中周日被视为一周的第1天! // 计算该月总天数(考虑闰年影响) int daysInMonth = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
可视化渲染逻辑
-
首行标注星期名称
创建固定字符串数组存储中文星期标识:String[] weekdays = {"日", "一", "二", "三", "四", "五", "六"}; for (String dayName : weekdays) { JLabel label = new JLabel(dayName); gridPanel.add(label); } -
填充空白占位符
若首日非周日(即索引>0),则在前几格插入空标签:for (int i = 0; i < firstDayOfWeek 1; i++) { gridPanel.add(new JLabel()); // 无文字的透明格子 } -
逐日添加交互元素
循环生成带数字的按钮或标签,支持点击事件监听:for (int day = 1; day <= daysInMonth; day++) { final int finalDay = day; // 用于Lambda表达式捕获变量值 JButton btn = new JButton(String.valueOf(day)); btn.addActionListener(e -> System.out.println("选中日期:" + finalDay)); gridPanel.add(btn); }
高级特性扩展
| 功能模块 | 实现方式 | 作用效果 |
|---|---|---|
| 实时时钟同步 | 结合Timer定时器每秒刷新界面显示 |
动态更新时间信息 |
| 主题切换 | 通过修改UIManager默认属性改变整体配色方案 | 暗黑/明亮模式自由切换 |
| 节假日标记 | 根据预存的特殊日期列表高亮对应单元格 | 直观展示法定休息日 |
| 跨月导航 | 栏两侧添加“<”“>”按钮触发前后月份切换 | 快速浏览相邻月份日程安排 |
| 事件提醒绑定 | 右键菜单关联备忘录编辑框,允许用户为特定日期添加备注 | 增强个人事务管理能力 |
完整示例代码整合
import javax.swing.;
import java.awt.;
import java.util.Calendar;
import java.util.Date;
public class MyCalendar extends JFrame {
private JLabel titleLabel; // 显示年月的大标题
private JPanel dayGrid; // 存放日期按钮的网格面板
private Calendar currentCal; // 当前操作的日历实例
public MyCalendar() {
initComponents(); // 组件初始化入口
loadCurrentMonthData(); // 加载初始数据
}
private void initComponents() {
// ===== 整体布局配置 =====
setLayout(new BorderLayout());
setSize(500, 400);
setVisible(true);
// ===== 头部年份显示区 =====
titleLabel = new JLabel("", SwingConstants.CENTER);
titleLabel.setFont(new Font("SansSerif", Font.PLAIN, 24));
add(titleLabel, BorderLayout.NORTH);
// ===== 中央日历表格区 =====
dayGrid = new JPanel(new GridLayout(6, 7)); // 最多6行×7列足够容纳所有情况
add(dayGrid, BorderLayout.CENTER);
// ===== 底部控制按钮区 =====
JPanel controlPane = new JPanel();
JButton preMonthBtn = new JButton("上个月");
JButton nextMonthBtn = new JButton("下个月");
controlPane.add(preMonthBtn);
controlPane.add(nextMonthBtn);
add(controlPane, BorderLayout.SOUTH);
// ===== 事件监听绑定 =====
preMonthBtn.addActionListener(e -> navigateMonth(-1));
nextMonthBtn.addActionListener(e -> navigateMonth(1));
}
private void loadCurrentMonthData() {
currentCal = Calendar.getInstance();
updateTitle(); // 更新标题栏文本
renderDayCells(); // 重新绘制所有日期单元格
}
private void updateTitle() {
String pattern = "yyyy年MM月";
SimpleDateFormat formatter = new SimpleDateFormat(pattern);
titleLabel.setText(formatter.format(currentCal.getTime()));
}
private void renderDayCells() {
dayGrid.removeAll(); // 清空现有内容
int startPos = currentCal.get(Calendar.DAY_OF_WEEK) 1; // 转换为0基索引
int totalDays = currentCal.getActualMaximum(Calendar.DAY_OF_MONTH);
// 添加星期表头
String[] weekdayNames = {"日", "一", "二", "三", "四", "五", "六"};
for (String name : weekdayNames) {
JLabel header = new JLabel(name);
header.setHorizontalAlignment(SwingConstants.CENTER);
dayGrid.add(header);
}
// 前置空白填充(当月第一天不是周日时)
for (int i = 0; i < startPos; i++) {
dayGrid.add(new JLabel());
}
// 正式日期项生成
for (int d = 1; d <= totalDays; d++) {
JButton dayBtn = new JButton(String.valueOf(d));
dayBtn.setPreferredSize(new Dimension(40, 40)); // 统一按钮大小
final int finalDay = d; // 解决闭包变量捕获问题
dayBtn.addActionListener(ev -> selectedDateChanged(finalDay));
dayGrid.add(dayBtn);
}
revalidate(); // 通知容器重新布局组件
repaint(); // 强制重绘可视区域
}
private void selectedDateChanged(int selectedDay) {
System.out.printf("您选择了 %d号n", selectedDay);
// TODO: 此处可扩展更多业务逻辑,如弹出详情对话框等
}
private void navigateMonth(int offset) {
currentCal.add(Calendar.MONTH, offset); // +1表示下个月,-1表示上个月
loadCurrentMonthData(); // 重新加载新月份的数据视图
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new MyCalendar()); // 确保EDT线程安全启动GUI程序
}
}
FAQs
Q1: 为什么运行后看不到任何日期?
A: 检查是否遗漏了revalidate()和repaint()调用,这两个方法必须成对出现才能触发Swing组件树的重构与重绘流程,特别是在动态修改面板子元素后(如removeAll()操作),必须显式通知布局管理器更新视图。
Q2: 如何让日历支持多语言显示?
A: 将硬编码的文字提取到资源文件中(如.properties格式),利用ResourceBundle类实现国际化适配,例如定义英文版资源文件CalendarEN.properties为:weekdays=Sun,Mon,Tue,Wed,Thu,Fri,Sat,运行时根据系统默认语言环境自动加载对应
