java設(shè)計(jì)日歷可視化的詳細(xì)步驟記錄
設(shè)計(jì)目標(biāo)
設(shè)計(jì)的這個(gè)日歷既可以捕捉當(dāng)天的日期,并且使用紅色圓圈圈起來以及實(shí)時(shí)具體時(shí)間,而且將月份和年份設(shè)計(jì)了下拉框,可以自行選擇具體的月份年份,也可以通過<和>兩個(gè)按鍵實(shí)現(xiàn)對月份的轉(zhuǎn)變,同時(shí)年份和月份的顯示會(huì)隨著按鍵轉(zhuǎn)變而轉(zhuǎn)變,本月日期用黑色標(biāo)記清楚,其他月份日期用灰色標(biāo)出。
實(shí)現(xiàn)功能
一、日歷展示功能
- 可視化日歷:創(chuàng)建一個(gè)可視化的日歷窗口,展示特定月份的日歷布局,包括星期幾的標(biāo)題行和日期格子。
- 月份切換:通過 “<” 和 “>” 按鈕可以切換到上一個(gè)月或下一個(gè)月的日歷。同時(shí),也可以通過年和月的下拉框選擇特定的年份和月份來顯示相應(yīng)的日歷。
- 突出顯示今天:在日歷中,用紅色圓形突出顯示當(dāng)天的日期,方便用戶快速識(shí)別。
- 顯示月份名稱:在窗口頂部顯示當(dāng)前月份的中文名稱,如 “一月”“二月” 等。
- 日期范圍調(diào)整:對于當(dāng)前月份之前的日期(上一個(gè)月的日期),以灰色字體顯示在日歷的開頭部分;對于當(dāng)前月份之后的日期(下一個(gè)月的占位符),也以灰色字體顯示在日歷的結(jié)尾部分。
二、年份選擇功能
年下拉框:創(chuàng)建一個(gè)包含當(dāng)前年份前后各 20 年的年份下拉框,用戶可以從下拉框中選擇特定的年份,選擇后會(huì)自動(dòng)更新顯示相應(yīng)年份的日歷。
三、月份選擇功能
月下拉框:創(chuàng)建一個(gè)包含十二個(gè)中文月份名稱的下拉框,用戶可以選擇不同的月份來顯示對應(yīng)月份的日歷。
四、實(shí)時(shí)時(shí)間顯示功能
時(shí)間更新:使用定時(shí)器每秒鐘更新一次窗口標(biāo)題,顯示當(dāng)前的時(shí)間,格式為 “yyyy-MM-dd HH:mm:ss”,并帶有時(shí)區(qū)設(shè)置為 “GMT+8”。
五、窗口布局和定位功能
- 窗口布局:使用
BorderLayout布局將窗口分為頂部和中央兩部分,頂部放置年、月下拉框、按鈕和年份標(biāo)簽,中央放置日歷面板。 - 窗口定位:在程序啟動(dòng)時(shí),自動(dòng)計(jì)算窗口在屏幕中的位置,使其居中顯示。
設(shè)計(jì)類的思想
- 可視化日歷應(yīng)用:CalendarMain類旨在創(chuàng)建一個(gè)可視化的日歷應(yīng)用程序,提供用戶交互功能,允許用戶通過年、月下拉框和按鈕切換不同的月份和年份,同時(shí)實(shí)時(shí)顯示當(dāng)前時(shí)間。
- 模塊化設(shè)計(jì):將不同的功能模塊分離,如年、月下拉框的創(chuàng)建和事件處理、日歷面板的生成、時(shí)間更新等分別封裝在不同的方法中,提高代碼的可讀性和可維護(hù)性。
- 用戶交互性:通過提供直觀的用戶界面,包括按鈕和下拉框,使用戶能夠方便地瀏覽不同的日期。同時(shí),通過自定義的圓形標(biāo)簽突出顯示今天的日期,增強(qiáng)用戶體驗(yàn)。
CalendarMain 類
整體設(shè)計(jì)目標(biāo)
CalendarMain 類的主要目標(biāo)是創(chuàng)建一個(gè)具有圖形用戶界面(GUI)的日歷應(yīng)用程序。它允許用戶查看指定月份和年份的日歷信息,能夠切換月份,并且在窗口標(biāo)題中實(shí)時(shí)顯示當(dāng)前時(shí)間。
成員變量設(shè)計(jì)思想
monthLabel:用于直觀地向用戶展示當(dāng)前所查看日歷的月份信息。通過JLabel來實(shí)現(xiàn),初始化為特定的月份字符串(如 "October 2023"),后續(xù)會(huì)根據(jù)用戶的操作(如切換月份)動(dòng)態(tài)更新其顯示內(nèi)容。yearLabel:與monthLabel類似,用于顯示當(dāng)前年份。同樣是JLabel類型,其顯示內(nèi)容會(huì)隨著用戶在年下拉框中的選擇或月份切換等操作而更新。calendarPanel:這是整個(gè)日歷應(yīng)用程序的核心顯示區(qū)域,通過JPanel實(shí)現(xiàn),并設(shè)置為GridLayout(7, 7)的網(wǎng)格布局。該布局用于以一種規(guī)則的方式展示日歷中的星期標(biāo)題以及每個(gè)月的日期信息等內(nèi)容。currentMonth和currentYear:分別用于記錄當(dāng)前所顯示日歷的月份索引(0 - 11 分別對應(yīng)一月到十二月)和年份信息。這兩個(gè)變量在程序運(yùn)行過程中會(huì)根據(jù)用戶的操作(如切換月份、選擇年份)不斷更新,以便準(zhǔn)確顯示相應(yīng)的日歷內(nèi)容。currentTime:用于存儲(chǔ)當(dāng)前時(shí)間的字符串表示。通過定時(shí)器定期更新這個(gè)變量的值,并將其用于更新窗口標(biāo)題,以實(shí)現(xiàn)實(shí)時(shí)顯示當(dāng)前時(shí)間的功能。yearComboBox和yearComboBox:這兩個(gè)下拉框組件分別用于用戶選擇年份和月份。yearComboBox包含了從當(dāng)前年份前后各 20 年的年份選項(xiàng),方便用戶在較大時(shí)間范圍內(nèi)選擇想要查看日歷的年份。monthComboBox則包含了中文月份名稱數(shù)組中的各個(gè)月份名稱,使用戶能夠以更直觀的方式選擇月份。
構(gòu)造函數(shù)設(shè)計(jì)思想
- 在
CalendarMain的構(gòu)造函數(shù)中,首先進(jìn)行了一系列窗口基本設(shè)置:- 創(chuàng)建并初始化
monthLabel和yearLabel,設(shè)置窗口標(biāo)題、大小以及關(guān)閉操作等,為整個(gè)應(yīng)用程序的窗口外觀和基本行為奠定基礎(chǔ)。 - 設(shè)置窗口布局為
BorderLayout,這種布局方式便于將不同功能的組件放置在窗口的不同區(qū)域(如頂部、中央等),使得界面布局更加清晰和有條理。 - 通過獲取屏幕尺寸并計(jì)算,將窗口設(shè)置為在屏幕居中顯示,提供更好的用戶體驗(yàn)。
- 創(chuàng)建并初始化
- 接著,獲取當(dāng)前時(shí)間的日歷對象,并從中提取當(dāng)前月份索引和年份信息,用于初始化
currentMonth和currentYear變量,確保應(yīng)用程序一開始顯示的是當(dāng)前時(shí)間對應(yīng)的日歷信息。 - 然后,分別創(chuàng)建并設(shè)置年下拉框和月下拉框:
- 對于年下拉框
yearComboBox,通過循環(huán)添加從當(dāng)前年份前后各 20 年的年份選項(xiàng),設(shè)置初始選中項(xiàng)為當(dāng)前年份,并為其添加動(dòng)作監(jiān)聽器。當(dāng)用戶選擇不同年份時(shí),監(jiān)聽器會(huì)觸發(fā)相應(yīng)的操作,更新currentYear變量并重新顯示日歷內(nèi)容。 - 對于月下拉框
monthComboBox,創(chuàng)建包含中文月份名稱的數(shù)組,并將各個(gè)月份名稱添加到下拉框中,設(shè)置初始選中項(xiàng)為當(dāng)前月份對應(yīng)的中文名稱。同樣為其添加動(dòng)作監(jiān)聽器,當(dāng)用戶選擇不同月份時(shí),監(jiān)聽器會(huì)觸發(fā)更新currentMonth變量并重新顯示日歷的操作。
- 對于年下拉框
- 之后,創(chuàng)建頂部面板
topPanel,并設(shè)置其布局為FlowLayout,用于放置上一個(gè)月按鈕、年下拉框、月下拉框、年份標(biāo)簽以及下一個(gè)月按鈕等組件。為上一個(gè)月按鈕和下一個(gè)月按鈕分別添加動(dòng)作監(jiān)聽器,點(diǎn)擊按鈕時(shí)會(huì)調(diào)用changeMonth方法來實(shí)現(xiàn)月份的切換操作。 - 最后,創(chuàng)建用于顯示日歷內(nèi)容的
calendarPanel,將其添加到窗口的中央?yún)^(qū)域,并調(diào)用displayCalendar方法首次顯示日歷內(nèi)容。同時(shí),創(chuàng)建一個(gè)定時(shí)器,每 1000 毫秒(1 秒)觸發(fā)一次updateTime方法,用于更新窗口標(biāo)題顯示的時(shí)間。
CircleLabel 類(內(nèi)部靜態(tài)類)
整體設(shè)計(jì)目標(biāo)
CircleLabel 類是一個(gè)自定義的標(biāo)簽類,繼承自 JLabel。其主要設(shè)計(jì)目標(biāo)是在日歷應(yīng)用程序中,針對表示當(dāng)天日期的標(biāo)簽進(jìn)行特殊繪制,使其呈現(xiàn)出圓形邊框的效果,以便用戶能夠更直觀地識(shí)別當(dāng)天日期。
成員變量設(shè)計(jì)思想
isToday:這個(gè)布爾類型的變量用于標(biāo)記該標(biāo)簽所表示的日期是否為今天。通過這個(gè)變量,在繪制標(biāo)簽時(shí)可以根據(jù)其值來決定是否繪制紅色圓形邊框,從而實(shí)現(xiàn)對今天日期的特殊標(biāo)識(shí)。
構(gòu)造函數(shù)設(shè)計(jì)思想
- 在構(gòu)造函數(shù)
CircleLabel(String text, boolean isToday)中:- 首先調(diào)用父類
JLabel的構(gòu)造函數(shù),設(shè)置文本居中顯示,確保日期文本在標(biāo)簽內(nèi)的顯示效果良好。 - 然后初始化
isToday變量,將傳入的參數(shù)值賦給它,以便后續(xù)繪制操作能夠根據(jù)這個(gè)值進(jìn)行相應(yīng)的處理。 - 最后設(shè)置標(biāo)簽的字體為
Arial,普通樣式,字號(hào)為 20,統(tǒng)一了日期標(biāo)簽的字體格式,使整個(gè)日歷的顯示更加規(guī)范和美觀。
- 首先調(diào)用父類
paintComponent 方法設(shè)計(jì)思想
- 該方法重寫了父類
JLabel的paintComponent方法。 - 首先,調(diào)用父類的
paintComponent方法進(jìn)行默認(rèn)繪制,保證標(biāo)簽的基本顯示內(nèi)容(如日期文本)能夠正常顯示。 - 然后,根據(jù)
isToday變量的值來決定是否進(jìn)行特殊繪制:如果isToday為true,則將繪制圖形的上下文轉(zhuǎn)換為Graphics2D,并設(shè)置抗鋸齒渲染提示,使繪制的圓形更平滑。接著,通過獲取標(biāo)簽的寬度和高度,計(jì)算出合適的圓形直徑以及圓心坐標(biāo),設(shè)置繪制顏色為紅色,最后繪制出紅色圓形邊框,從而實(shí)現(xiàn)了對今天日期的特殊標(biāo)識(shí)效果。
displayCalendar 方法設(shè)計(jì)思想
- 該方法的主要目的是根據(jù)當(dāng)前的
currentYear和currentMonth變量的值,準(zhǔn)確地在calendarPanel上顯示相應(yīng)月份的日歷信息。 - 首先,清除
calendarPanel上的所有組件,為重新顯示新的日歷內(nèi)容做好準(zhǔn)備。 - 然后,創(chuàng)建當(dāng)前月份的日歷對象和上一個(gè)月的日歷對象,用于獲取相關(guān)的日歷信息,如當(dāng)前月的第一天是星期幾、當(dāng)前月和上一個(gè)月的總天數(shù)等。
- 接著,更新
monthLabel和yearLabel的顯示內(nèi)容,使其準(zhǔn)確反映當(dāng)前所查看的月份和年份信息。 - 之后,添加星期標(biāo)題行,通過循環(huán)將星期的中文簡稱(如 “一”、“二” 等)添加到
calendarPanel上作為日歷的標(biāo)題行。 - 再根據(jù)獲取到的當(dāng)前月的第一天是星期幾以及當(dāng)前月和上一個(gè)月的總天數(shù)等信息,分三步添加日歷中的日期信息:
- 添加上一個(gè)月的空白日期(灰色顯示):通過循環(huán),根據(jù)當(dāng)前月第一天是星期幾的信息,計(jì)算出需要添加上一個(gè)月的哪些空白日期,并將這些日期以灰色顯示的方式添加到
calendarPanel上。 - 添加當(dāng)前月的日期:通過循環(huán)遍歷當(dāng)前月的所有日期,對于每個(gè)日期,判斷其是否為今天,如果是則使用
CircleLabel類創(chuàng)建帶有圓形邊框標(biāo)識(shí)的標(biāo)簽,否則使用普通的JLabel創(chuàng)建標(biāo)簽,然后將這些標(biāo)簽添加到calendarPanel上。 - 填充后續(xù)的空白日期(灰色顯示):根據(jù)日歷布局的規(guī)則以及當(dāng)前月的總天數(shù)和第一天是星期幾等信息,計(jì)算出需要填充的后續(xù)空白日期,并將這些日期以灰色顯示的方式添加到
calendarPanel上。
- 添加上一個(gè)月的空白日期(灰色顯示):通過循環(huán),根據(jù)當(dāng)前月第一天是星期幾的信息,計(jì)算出需要添加上一個(gè)月的哪些空白日期,并將這些日期以灰色顯示的方式添加到
- 最后,重新驗(yàn)證和重繪
calendarPanel,確保新添加的組件能夠正確顯示在窗口中,并且顯示效果良好。
changeMonth 方法設(shè)計(jì)思想
- 該方法用于實(shí)現(xiàn)月份的切換功能。當(dāng)用戶點(diǎn)擊上一個(gè)月按鈕或下一個(gè)月按鈕時(shí),會(huì)傳入正負(fù) 1 的參數(shù)值來表示切換到上一個(gè)月或下一個(gè)月。
- 根據(jù)傳入的參數(shù)值對
currentMonth變量進(jìn)行相應(yīng)的調(diào)整:如果currentMonth小于 0,說明要切換到上一年的 12 月份,此時(shí)將currentMonth設(shè)為 11,同時(shí)將currentYear減 1;如果currentMonth大于 11,說明要切換到下一年的 1 月份,此時(shí)將currentMonth設(shè)為 0,同時(shí)將currentYear增 1。 - 然后,更新年下拉框和月下拉框的選中項(xiàng),使其與新的
currentYear和currentMonth相匹配。 - 最后,調(diào)用
displayCalendar方法重新顯示日歷內(nèi)容,以反映出月份切換后的新情況。
main 方法設(shè)計(jì)思想
- 在
main方法中,使用SwingUtilities.invokeLater方法來確保在事件分發(fā)線程(EDT)中創(chuàng)建和顯示窗口。這是因?yàn)?Swing 組件必須在 EDT 中進(jìn)行創(chuàng)建和操作,以避免出現(xiàn)線程相關(guān)的問題。 - 在
invokeLater方法內(nèi)部,創(chuàng)建了CalendarMain類的一個(gè)實(shí)例,并設(shè)置其可見,從而啟動(dòng)了整個(gè)日歷應(yīng)用程序,使得用戶可以通過圖形界面查看日歷信息、切換月份以及看到實(shí)時(shí)更新的時(shí)間顯示。
updateTime 方法設(shè)計(jì)思想
- 該方法的主要目的是更新窗口標(biāo)題顯示的時(shí)間。
- 首先,創(chuàng)建一個(gè)
SimpleDateFormat格式器,設(shè)置其格式為 "yyyy-MM-dd HH:mm:ss",并將其時(shí)區(qū)設(shè)置為 "GMT+8"(東八區(qū),符合中國等多數(shù)地區(qū)的時(shí)間標(biāo)準(zhǔn))。 - 然后,通過調(diào)用
SimpleDateFormat的format方法,將當(dāng)前日期和時(shí)間(通過new java.util.Date()獲取)格式化為指定格式的字符串,并將其存儲(chǔ)到currentTime變量中。 - 最后,將窗口標(biāo)題更新為 "日歷" +
currentTime,實(shí)現(xiàn)了在窗口標(biāo)題中實(shí)時(shí)顯示當(dāng)前時(shí)間的功能。
getMonthName 方法設(shè)計(jì)思想
- 該方法的主要目的是根據(jù)月份索引(0 - 11)獲取對應(yīng)的中文月份名稱。
- 通過創(chuàng)建一個(gè)包含中文月份名稱的數(shù)組,然后根據(jù)傳入的月份索引從數(shù)組中獲取相應(yīng)的中文月份名稱并返回,為在其他方法(如
changeMonth方法中更新月下拉框選中項(xiàng))中提供了方便的獲取中文月份名稱的方式。
設(shè)計(jì)原理
.窗口布局和組件創(chuàng)建:
- 使用JFrame作為主窗口,設(shè)置標(biāo)題、大小、關(guān)閉操作和布局。通過BorderLayout布局將窗口分為頂部和中央兩部分,頂部放置年、月下拉框和按鈕,中央放置日歷面板。
- 創(chuàng)建JLabel作為月份和年份標(biāo)簽,以及JPanel作為頂部面板和日歷面板。使用FlowLayout布局管理頂部面板,GridLayout布局管理日歷面板。
- 創(chuàng)建年、月下拉框,并添加相應(yīng)的動(dòng)作監(jiān)聽器,以便在用戶選擇不同的年份和月份時(shí)更新日歷顯示。
2.日歷生成:
- 使用GregorianCalendar類獲取當(dāng)前時(shí)間和特定月份的日歷信息。通過計(jì)算當(dāng)前月的第一天是星期幾以及該月的總天數(shù),生成日歷的布局。
- 對于上一個(gè)月和下一個(gè)月的空白日期,使用灰色字體顯示上一個(gè)月的日期或下一個(gè)月的占位符。對于當(dāng)前月的日期,使用自定義的圓形標(biāo)簽突出顯示今天的日期。
3.用戶交互和時(shí)間更新:
- 為上一個(gè)月和下一個(gè)月按鈕添加動(dòng)作監(jiān)聽器,實(shí)現(xiàn)月份的切換功能。在切換月份時(shí),更新當(dāng)前月份和年份,并重新顯示日歷。
- 使用Timer類創(chuàng)建定時(shí)器,每秒鐘觸發(fā)一次時(shí)間更新操作,更新窗口標(biāo)題顯示的當(dāng)前時(shí)間。
功能實(shí)現(xiàn)





設(shè)計(jì)源碼
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.GregorianCalendar;
import java.awt.RenderingHints;
// 定義主類 CalendarMain,繼承自 JFrame 以創(chuàng)建可視化窗口
public class CalendarMain extends JFrame {
// 月份標(biāo)簽,用于顯示當(dāng)前月份
private final JLabel monthLabel;
// 年份標(biāo)簽,用于顯示當(dāng)前年份
private final JLabel yearLabel;
// 日歷面板,用于顯示日歷內(nèi)容
private final JPanel calendarPanel;
// 當(dāng)前月份的索引
private int currentMonth;
// 當(dāng)前年份
private int currentYear;
// 當(dāng)前時(shí)間的字符串表示
private static String currentTime;
// 年下拉框組件
private JComboBox<Integer> yearComboBox;
// 月下拉框組件
private JComboBox<String> monthComboBox;
public CalendarMain() {
// 創(chuàng)建月份標(biāo)簽,初始顯示為 "October 2023"
monthLabel = new JLabel("October 2023");
// 設(shè)置窗口標(biāo)題為 "日歷"
setTitle("日歷");
// 設(shè)置窗口大小為 500x450 像素
setSize(500, 450);
// 設(shè)置窗口關(guān)閉操作,當(dāng)關(guān)閉窗口時(shí)退出程序
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 設(shè)置窗口布局為 BorderLayout(邊框布局)
setLayout(new BorderLayout());
// 獲取屏幕尺寸
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int screenWidth = screenSize.width;
int screenHeight = screenSize.height;
// 計(jì)算窗口在屏幕中的位置,使其居中顯示
int x = (screenWidth - getWidth()) / 2;
int y = (screenHeight - getHeight()) / 2;
setLocation(x, y);
// 獲取當(dāng)前時(shí)間的日歷對象
Calendar calendar = new GregorianCalendar();
// 獲取當(dāng)前月份的索引
currentMonth = calendar.get(Calendar.MONTH);
// 獲取當(dāng)前年份
currentYear = calendar.get(Calendar.YEAR);
// 創(chuàng)建年下拉框
yearComboBox = new JComboBox<>();
// 循環(huán)添加從當(dāng)前年份前后各 20 年的年份選項(xiàng)到年下拉框
for (int i = currentYear - 20; i <= currentYear + 20; i++) {
yearComboBox.addItem(i);
}
// 設(shè)置年下拉框的初始選中項(xiàng)為當(dāng)前年份
yearComboBox.setSelectedItem(currentYear);
// 為年下拉框添加動(dòng)作監(jiān)聽器,當(dāng)選擇年份變化時(shí)更新當(dāng)前年份并重新顯示日歷
yearComboBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
currentYear = (Integer) yearComboBox.getSelectedItem();
displayCalendar();
}
});
// 創(chuàng)建月下拉框
monthComboBox = new JComboBox<>();
// 創(chuàng)建包含中文月份名稱的數(shù)組
String[] monthNames = new String[]{"一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"};
// 循環(huán)添加中文月份名稱到月下拉框
for (int i = 0; i < 12; i++) {
monthComboBox.addItem(monthNames[i]);
}
// 設(shè)置月下拉框的初始選中項(xiàng)為當(dāng)前月份對應(yīng)的中文名稱
monthComboBox.setSelectedItem(monthNames[currentMonth]);
// 為月下拉框添加動(dòng)作監(jiān)聽器,當(dāng)選擇月份變化時(shí)更新當(dāng)前月份并重新顯示日歷
monthComboBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
currentMonth = monthComboBox.getSelectedIndex();
displayCalendar();
}
});
// 創(chuàng)建頂部面板,用于放置年、月下拉框和按鈕等
JPanel topPanel = new JPanel();
topPanel.setLayout(new FlowLayout());
// 創(chuàng)建上一個(gè)月按鈕,點(diǎn)擊時(shí)調(diào)用 changeMonth(-1) 方法切換到上一個(gè)月
JButton prevButton = new JButton("<");
prevButton.addActionListener(e -> changeMonth(-1));
topPanel.add(prevButton);
// 將年下拉框添加到頂部面板
topPanel.add(yearComboBox);
// 將月下拉框添加到頂部面板
topPanel.add(monthComboBox);
// 創(chuàng)建年份標(biāo)簽(這里暫時(shí)保留原有的年份標(biāo)簽顯示方式)
yearLabel = new JLabel();
topPanel.add(yearLabel);
// 創(chuàng)建下一個(gè)月按鈕,點(diǎn)擊時(shí)調(diào)用 changeMonth(1) 方法切換到下一個(gè)月
JButton nextButton = new JButton(">");
nextButton.addActionListener(e -> changeMonth(1));
topPanel.add(nextButton);
// 將頂部面板添加到窗口的北側(cè)(BorderLayout.NORTH)
add(topPanel, BorderLayout.NORTH);
// 創(chuàng)建一個(gè) 6 行 7 列的網(wǎng)格布局的面板,用于顯示日歷內(nèi)容
calendarPanel = new JPanel(new GridLayout(7, 7));
// 將日歷面板添加到窗口的中央(BorderLayout.CENTER)
add(calendarPanel, BorderLayout.CENTER);
// 顯示日歷內(nèi)容
displayCalendar();
// 創(chuàng)建一個(gè)定時(shí)器,每 1000 毫秒(1 秒)觸發(fā)一次更新時(shí)間的操作
Timer timer = new Timer(1000, this::updateTime);
timer.start();
}
// 自定義的圓形標(biāo)簽類,繼承自 JLabel
private static class CircleLabel extends JLabel {
// 表示是否為今天的標(biāo)志
private boolean isToday;
public CircleLabel(String text, boolean isToday) {
// 調(diào)用父類構(gòu)造方法,設(shè)置文本居中顯示
super(text, SwingConstants.CENTER);
this.isToday = isToday;
// 設(shè)置字體為 Arial,普通樣式,字號(hào)為 20
setFont(new Font("Arial", Font.PLAIN, 20));
}
@Override
protected void paintComponent(Graphics g) {
// 調(diào)用父類的 paintComponent 方法進(jìn)行默認(rèn)繪制
super.paintComponent(g);
// 如果是今天,則繪制紅色圓形
if (isToday) {
Graphics2D g2d = (Graphics2D) g;
// 設(shè)置抗鋸齒渲染提示,使繪制的圓形更平滑
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int width = getWidth();
int height = getHeight();
int diameter = Math.min(width, height);
int x = (width - diameter) / 2;
int y = (height - diameter) / 2;
// 設(shè)置繪制顏色為紅色
g2d.setColor(Color.RED);
// 繪制圓形
g2d.drawOval(x, y, diameter, diameter);
}
}
}
private void displayCalendar() {
// 清除日歷面板上的所有組件
calendarPanel.removeAll();
// 創(chuàng)建當(dāng)前月份的日歷對象
Calendar calendar = new GregorianCalendar(currentYear, currentMonth, 1);
// 創(chuàng)建上一個(gè)月的日歷對象
Calendar prevMonth = new GregorianCalendar();
if (currentMonth == 0) {
// 如果當(dāng)前月份是一月,則上一個(gè)月為去年的十二月
prevMonth.set(currentYear - 1, 11, 1);
} else {
// 否則,上一個(gè)月為當(dāng)前月份減一
prevMonth.set(currentYear, currentMonth - 1, 1);
}
// 更新月份標(biāo)簽為當(dāng)前月份的長名稱
monthLabel.setText(calendar.getDisplayName(Calendar.MONTH, Calendar.LONG, getLocale()));
// 更新年份標(biāo)簽為當(dāng)前年份
yearLabel.setText(String.valueOf(currentYear));
// 添加星期標(biāo)題行
String[] days = {"一", "二", "三", "四", "五", "六", "天"};
for (String day : days) {
// 創(chuàng)建星期標(biāo)題標(biāo)簽并添加到日歷面板
calendarPanel.add(new JLabel(day, SwingConstants.CENTER));
}
// 獲取當(dāng)前月的第一天是星期幾(星期日為 1,星期六為 7)
int startDay = calendar.get(Calendar.DAY_OF_WEEK) - 1;
if (startDay == 0) startDay = 7;
// 獲取當(dāng)前月的總天數(shù)
int daysInMonth = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
// 獲取上一個(gè)月的總天數(shù)
int prevDaysInMonth = prevMonth.getActualMaximum(Calendar.DAY_OF_MONTH);
// 獲取當(dāng)前日期的日歷對象
Calendar todayCalendar = new GregorianCalendar();
int todayDay = todayCalendar.get(Calendar.DAY_OF_MONTH);
int todayMonth = todayCalendar.get(Calendar.MONTH);
int todayYear = todayCalendar.get(Calendar.YEAR);
// 添加上一個(gè)月的空白日期(灰色顯示)
for (int i = startDay - 1; i >= 1; i--) {
JLabel label = new JLabel(String.valueOf(prevDaysInMonth - i + 1), SwingConstants.CENTER);
label.setForeground(Color.GRAY);
label.setFont(new Font("Arial", Font.PLAIN, 20));
calendarPanel.add(label);
}
// 添加當(dāng)前月的日期
for (int i = 1; i <= daysInMonth; i++) {
boolean isToday = (i == todayDay) && (currentMonth == todayMonth) && (currentYear == todayYear);
// 創(chuàng)建日期標(biāo)簽,如果是今天則用圓形標(biāo)簽顯示
calendarPanel.add(new CircleLabel(String.valueOf(i), isToday));
}
// 填充后續(xù)的空白日期(灰色顯示)
for (int i = 1; i <= 42 - daysInMonth - (startDay - 1); i++) {
JLabel dayLabel = new JLabel(String.valueOf(i), SwingConstants.CENTER);
dayLabel.setForeground(Color.GRAY);
dayLabel.setFont(new Font("Arial", Font.PLAIN, 20));
calendarPanel.add(dayLabel);
}
// 重新驗(yàn)證和重繪日歷面板
calendarPanel.revalidate();
calendarPanel.repaint();
}
// 切換月份的方法,delta 為正負(fù) 1 表示切換到上一個(gè)月或下一個(gè)月
private void changeMonth(int delta) {
currentMonth += delta;
if (currentMonth < 0) {
currentMonth = 11;
currentYear--;
} else if (currentMonth > 11) {
currentMonth = 0;
currentYear++;
}
// 更新年下拉框的選中項(xiàng)
yearComboBox.setSelectedItem(currentYear);
// 更新月下拉框的選中項(xiàng)
monthComboBox.setSelectedItem(getMonthName(currentMonth));
// 重新顯示日歷
displayCalendar();
}
public static void main(String[] args) {
// 使用 SwingUtilities.invokeLater 確保在事件分發(fā)線程中創(chuàng)建和顯示窗口
SwingUtilities.invokeLater(() -> {
CalendarMain app = new CalendarMain();
app.setVisible(true);
});
}
// 更新時(shí)間的方法,每秒鐘更新一次窗口標(biāo)題顯示的時(shí)間
private void updateTime(ActionEvent e) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setTimeZone(java.util.TimeZone.getTimeZone("GMT+8"));
currentTime = sdf.format(new java.util.Date());
setTitle("日歷" + currentTime);
}
// 根據(jù)月份索引獲取中文月份名稱的方法
private String getMonthName(int monthIndex) {
String[] monthNames = new String[]{"一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"};
return monthNames[monthIndex];
}
}總結(jié)
到此這篇關(guān)于java設(shè)計(jì)日歷可視化的文章就介紹到這了,更多相關(guān)java日歷可視化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Maven 打包項(xiàng)目到私服 (deploy)的配置方法
這篇文章主要介紹了Maven 打包項(xiàng)目到私服 (deploy)的配置方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07
SpringCloudStream原理和深入使用小結(jié)
Spring?Cloud?Stream是一個(gè)用于構(gòu)建與共享消息傳遞系統(tǒng)連接的高度可擴(kuò)展的事件驅(qū)動(dòng)型微服務(wù)的框架,本文給大家介紹SpringCloudStream原理和深入使用,感興趣的朋友跟隨小編一起看看吧2024-06-06
Java17中record替代Lombok部分功能使用場景探究
這篇文章主要介紹了使用Java17中的record替代Lombok的部分功能,本文來為大家小小的總結(jié)下,我們可以在哪些地方,利用record來替換Lombok2024-01-01
jxls2.4.5如何動(dòng)態(tài)導(dǎo)出excel表頭與數(shù)據(jù)
這篇文章主要介紹了jxls2.4.5如何動(dòng)態(tài)導(dǎo)出excel表頭與數(shù)據(jù)問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08
Spring Boot之搞定mongoTemplate的知識(shí)小結(jié)
這篇文章主要介紹了Spring Boot之搞定mongoTemplate的知識(shí)小結(jié),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12
java使用鏈表實(shí)現(xiàn)約瑟夫環(huán)
這篇文章主要為大家詳細(xì)介紹了java使用鏈表實(shí)現(xiàn)約瑟夫環(huán),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-05-05

