本文介绍近期写的两个日历相关的组件,MonthlyCalendar和DateChooser,其使用方法,并附上一个demo程序。
『Demo文件』
附件:CalendarDemo1.4.2_07.jar(36K)
Demo程序包含class文件和源代码,用j2sdk1.4.2_07编译打包。里面包含文中提到组件类的API文档的缩略版。详细文档可以用javadoc从demo文件中包含的源代码生成。
『MonthlyCalendar组件』
MonthlyCalenar是一个JComponent的子类(extends JComponent),按星期排列显示一个月的日期分部。其默认界面类似Windows XP默认界面的日历显示,如Figure 1(a) 所示。
同时,MonthlyCalendar实现了MouseListener和MouseMotionListener两个接口,用来监听其自身的鼠标事件。当鼠标停在一个日期时,该日期会以highlight方式显示。当鼠标在一个日期上点击时,可以认为是一个“日期选中事件”(为了简便,该事件并不是真正的AWT/Swing事件,只是一个假想的事件)发生。MonthlyCalendar里面有一个protected void dateSelected()的方法,其内容为空。当上述事件发生时(通过监控鼠标点击),该方法会被调用。因此,用户程序可以通过override该方法,来加入“事件响应代码”。
MonthlyCalendar提供了一些Bean Pattern的方法,提供个性化其界面风格的途径和日期的设置。总结来说,可设置的属性包括下面这些:
- Header(星期显示)的字体(font),背景色(background)和字体色(foreground)。
- Date(日期显示)的字体,背景色和字体色。
- 日期被highligh显示时的背景色和字体色。
- 星期是否从星期日开始。如果不是,星期将从星期一开始。默认从星期日开始。
- 单元格大小(Dimension),即每个日期或者星期几的显示所占的大小。整个日历的大小(preferredSize)是所有单元格的大小加上Border(如果设置了)的Insets。值得注意的是,直接设置MonthlyCalendar的preferredSize是无效的。因为它用一个空的setPreferredSize的方法override了JComponent的setPreferredSize方法。
- Header组件(JComponent)。如果觉得不喜欢默认的星期显示(S M T W T F S),可以自己做一个组件(只要是JComponent或者其子类都行),然后给日历装上。比如,可以做一个JPanel,里面放七个JLabel,每个标签分别写“Sun”,“Mon”……。当初之所以提供这个方法,最主要的还是想加入对中文的支持(一种变相的支持)。由于Java对中文的支持还是很有问题的,所以直接写中文字可能出现乱码。一个万无一失的方法就是直接把中文字放到一副图片中。所以,用户可以先把中文的星期显示制成一幅图片(其排列要跟星期开始的顺序符合),然后把该图片放到一个JLabel里面,给日历装上就行了。Figure 1(b)显示了一个安装了Header组件并且设置了一些其他属性的日历。
需要注意的是:一旦给日历安装了Header组件,单元格的大小将按照Header组件重新设置,规则是:单元格宽度等于Header组件宽度除以7,单元格高度等于Header组件高度;同时,整个日历的大小计算类似于设置单元格时的计算,只是其宽度还要加上Header组件宽度除以7的余数。
- 年份,月份的设置。
- 使用/弃用鼠标监听。默认使用。

下面这段简单的代码展示如何使用MonthlyCalendar。一个日历组件和一个textfield被放在一个frame里面,当日期被选中时,所选日期会被显示在textfield里面。
import hysun.util.MonthlyCalendar;
import javax.swing.*;
import java.awt.*;
public class TestMC extends JFrame {
public TestMC() {
final JTextField textfield = new JTextField();
getContentPane().add(textfield, BorderLayout.NORTH);
MonthlyCalendar mc = new MonthlyCalendar() {
protected void dateSelected() {
textfield.setText(getYear()+"."+getMonth()+"."+getSelectedDay());
}
};
mc.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
mc.setYearMonth(2005, 2);
getContentPane().add(mc);
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
}
public static void main(String args[]) {
new TestMC().setVisible(true);
}
}
对于MonthlyCalendar组件,用户可以弃用其日期选择的功能,只是使用其图形界面。demo程序里面第一个demo就是利用它生成一个年历。
以前也见到过别人写的一些日期选择组件,是在一个panel里面放了大量的label,通过GridLayout布局,每一个label用来显示一天或者显示一个星期日期的显示,由于需要创建大量的对象,需要较多资源。MonthlyCalendar本身是一个JComponent,它里面没有加入其他组件(headerComponent除外),其界面全部通过Graphics类画出来,所需资源要少。
『DateChooser组件』
该组件是基于上述MonthlyCalendar组件构建的一个日期选择器。它本身是一个JDialog的子类,对话框里面除了一个MonthlyCalendar组件,还加入了对年份和月份的控制,基本界面如Figure 2所示。

DateChooser组件的使用非常简单,用户程序所需要用到的方法只有四个(当然,记得先创建一个DateChooser对象,创建方法跟JDialog一样):
- public void setVisible(boolean b)。该方法从JDialog继承而来,加入代码使得每次调用的时候会把日历里面给highlight的日期恢复到正常显示。
- public void setYearMonth(int y, int m)。DateChooser刚被初始化的时候会自动设置到当前日期。在setVisible之前调用该方法,可以把日期设置到你所需要的那个月。
- public java.util.Calendar getSelectedDate()。该方法在setVisible之后调用,把所选中日期以一个Calendar对象封装返回。如果用户没有选中任何日期(即对话框被直接关掉),该方法返回null。
- public void setWeekStartOnSunday(boolean b)。该方法直接调用MonthlyCalendar的相应方法。
长话短说,一个简单的working example如下:
import hysun.util.DateChooser;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.text.SimpleDateFormat;
public class TestDC extends JFrame {
private static final SimpleDateFormat FORMATTER =
new SimpleDateFormat("yyyy.MM.dd");
public TestDC() {
final DateChooser dc = new DateChooser(this, true);
final JTextField textfield = new JTextField();
getContentPane().add(textfield, BorderLayout.NORTH);
JPanel panel = new JPanel();
JButton button = new JButton("Select Date");
panel.add(button);
getContentPane().add(panel);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
dc.setLocationRelativeTo(textfield);
dc.setVisible(true);
java.util.Calendar cal = dc.getSelectedDate();
if (cal != null) {
textfield.setText(FORMATTER.format(cal.getTime()));
}
}
});
pack();
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public static void main(String args[]) {
new TestDC().setVisible(true);
}
}
由于DateChooser旨在提供最方便快捷的日期选择器,它使用MonthlyCalendar默认的界面,不提供个性化的Bean方法。但是用户可以很方便的利用MonthlyCalendar来构建个性化的日期选择器,包括其年份月份控制部分(在IDE里面,利用MonthlyCalendar的Bean Pattern方法,这是非常简单的工作)。
|