今天要做一个类似Windows的时间属性的东西,原本以为是一个JComboBox中间放了一个JFormattedTextField,但是baidu之后,发现了一个更好用的,那就是JSpinner,哈哈。转一个帖子,写的很详细。原来的地址是:http://www.bitscn.com/java/swing/200605/20654.html
图示1.JSpinner实例
要创建和操纵JSpinner,许多类都将被调用,最重要的是JSpinner自己。最重要的两个准素集包括SpinnerModel接口,包括可选择的集合中的选项,还有,JSpinner.DefaultEditor的实现,用来捕获所有选择。庆幸的是,许多其它调用的类都是在后台工作的,比如,一旦你给SpinnerNumberModel提供了数字的范围,并且用这个类来协助Spinner,你的工作实际上是完成了。
创建JSpinner控件
JSpinner类包括两个构造函数来初始化控件:
public JSpinner()
JSpinner spinner = new JSpinner();
public JSpinner(SpinnerModel model)
SpinnerModel model = new SpinnerListModel(args);
JSpinner spinner = new JSpinner(model);
开始的时候可以没有数据模型,后面可以使用它来跟踪JSpinner的方法。另一个方法,在创建这个控件的时候使用完整的模型,实现SpinnerModel接口,它里面有三个具体的子类可以使用:SpinnerDateModel,SpinnerListModel和SpinnerNumberModel,伴随着他们的抽象父类AbstractSpinnerModel。如果不指名模型,那么SpinnerNumberModel将默认使用。而显示和编辑的控件是JFormattedTextField,编辑的基本功能是通过一系列JSpinner的内部类实现的:DateEditor,ListEditor和NumberFormat,还有父类中DefaultEditor的支持。
JSpinner属性
除了创建JSpinner对象之外,你还可以通过表一中的九个属性中的一个来进行配置。
Table 1. JSpinner 属性
value属性中的值允许你更改当前控件的设置,nextValue和perviousValue可以使你以不同的方向察看模型中的入口。
使用ChangeListener来监听JSpinner events
JSpinner直接支持一种事件监听:changeListener。在别的地方,当commitEdit()方法被调用,这个事件将被触发,告诉你spinner的值发生改变。为了证明,列表1联系到一个自定义的ChangeListener,与图示1的程序相关联。
列表 1. JSpinner with ChangeListener
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
import java.text.*;
import java.util.*;
public class SpinnerSample {
public static void main (String args[]) {
Runnable runner = new Runnable() {
public void run() {
JFrame frame = new JFrame("JSpinner Sample");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DateFormatSymbols symbols =
new DateFormatSymbols(Locale.FRENCH);
示图 2. JSpinner under different look-and-feel types
集合中的11个UIResource属性在表格2中列举,有限的方法绘制text field和箭头。
Table 2. JSpinner UIResource 元素
SpinnerModel 接口
到目前,我们已经看到了如何同一个主JSpinner类连接,SpinnerModel接口是控件的数据模型,SpinnerModel的定义如下:
public interface SpinnerModel {
// Properties
public Object getValue();
public void setValue(Object);
public Object getNextValue();
public Object getPreviousValue();
// Listeners
public void addChangeListener(ChangeListener);
public void removeChangeListener(ChangeListener);
}
SpinnerModel中的六个方法直接绘制了JSpinner,而JSpinner的方法间接调用模块中的方法,在监听的状况下,事件将联系到监听器。
AbstractSpinnerModel类
AbstractSpinnerModel类基本要实现的是SpinnerModel接口,它提供了管理和通知的监听列表,子类必须实现接口中的四个方法,SpinnerModel中的三个具体实现如下:SpinnerDateModel,SpinnerListModel和SpinnerNumberModel。
SpinnerDateModel类
从名字可以推断出,SpinnerDateModel提供了数据的选择。这个类有两个构造函数:一个默认选择所有的数据,另一个要求你给出范围。
SpinnerDateModel:用于接受日期输入。该类支持通过将 Calendar 类中的常数设置为不同的值来更改日期;例如,Calendar.WEEK_OF_MONTH 每次将日期更改一周。
public SpinnerDateModel()
SpinnerModel model = new SpinnerDateModel();
JSpinner spinner = new JSpinner(model);
public SpinnerDateModel(Date value, Comparable start, Comparable end,int calendarField)
Calendar cal = Calendar.getInstance();
Date now = cal.getTime();
cal.add(Calendar.YEAR, -50);
Date startDate = cal.getTime();
cal.add(Calendar.YEAR, 100);
Date endDate = cal.getTime();
SpinnerModel model = new SpinnerDateModel(now, startDate, endDate, Calendar.YEAR);
JSpinner spinner = new JSpinner(model);
如果不指名任何参数,就没有开始和结束点。下面的例子展示了使用参数来表示100年的范围。最后一个成员变量应该是Calendar类中的一个定值:
・ Calendar.AM_PM
・ Calendar.DAY_OF_MONTH
・ Calendar.DAY_OF_WEEK
・ Calendar.DAY_OF_WEEK_IN_MONTH
・ Calendar.DAY_OF_YEAR
・ Calendar.ERA
・ Calendar.HOUR
・ Calendar.HOUR_OF_DAY
・ Calendar.MILLISECOND
・ Calendar.MINUTE
・ Calendar.MONTH
・ Calendar.SECOND
・ Calendar.WEEK_OF_MONTH
・ Calendar.WEEK_OF_YEAR
・ Calendar.YEAR
注意:SpinnerDateModel不包含任何Calendar类中的时间域,所以不能通过SpinnerDateModel在JSpinner中翻转。
表格3列出了SpinnerModel中的三个属性,四个关于SpinnerDateModel。
Table 3. SpinnerDateModel 属性
典型地,唯一的新属性中你将要用来获得最终的日期,尽管所有的结果都被包裹在getValue()中,以适当的数据类型。如果在构造函数中提供了数据的表示范围,那么previous和next的值将是null,在边界条件下。
SpinnerListModel用于接受来自值列表的输入。提供了从一个入口列表中选择或者至少是字符串表述,这个类有三个构造函数:
public SpinnerListModel()SpinnerModel model = new SpinnerListModel();
JSpinner spinner = new JSpinner(model);
public SpinnerListModel(List <?> values)List <String> list = args;
SpinnerModel model = new SpinnerListModel(list);
JSpinner spinner = new JSpinner(model);
public SpinnerListModel(Object[] values)SpinnerModel model = new SpinnerListModel(args);
JSpinner spinner = new JSpinner(model);
当没有参数提供时,这个模型包括一个元素:字符串empty。List版保留一个对list的引用。而不是list的拷贝。如果改变了list,那么模型中的list也将改变。数组版本的创建了一个私有的内部类,并且实例化一个list。对于list和数组版本,初始选择的是第一个元素,否则将抛出一个IllegalArgumentException异常。
如表格4显示,属性中增添的是set和get list。
SpinnerNumberModel类
SpinnerNumberModel提供了从一个开区间或闭区间选择数字的模式,数字可以使Number类的所有子类,包括Integer和Double。他有四个构造函数。
public SpinnerNumberModel()SpinnerModel model = new SpinnerNumberModel();
JSpinner spinner = new JSpinner(model);
public SpinnerNumberModel(double value, double minimum, double maximum,
double stepSize)SpinnerModel model = new SpinnerNumberModel(50, 0, 100, .25);
JSpinner spinner = new JSpinner(model);
public SpinnerNumberModel(int value, int minimum, int maximum, int stepSize)SpinnerModel model = new SpinnerNumberModel(50, 0, 100, 1);
JSpinner spinner = new JSpinner(model);
public SpinnerNumberModel(Number value, Comparable minimum, Comparable maximum,
Number stepSize)Number value = new Integer(50);
Number min = new Integer(0);Number max = new Integer(100);
Number step = new Integer(1);SpinnerModel model = new SpinnerNumberModel(value, min, max, step);
JSpinner spinner = new JSpinner(model);
如果最大或最小值为null,则为开区间。对于没有参数的,初始值为1,步进为1。步进是整形的,如果你设为.333,那么将不会完成。
表格5展示了SpinnerNumberModel的属性:
自定义模型
一般来说,可用的JSpinner模型已经足够了,所以没有必要创建他的子类了。但是,并不是所有场合都能满足。比如,你可能希望使用一个包装了SpinnerListModel的模型,代替停止在第一个或最后一个元素,他包装了另一个结束。在列表2中给出了具体实现:
Listing 2. RolloverSpinnerListModel 类
import javax.swing.*;
import java.util.*;
public class RolloverSpinnerListModel extends SpinnerListModel {
public RolloverSpinnerListModel(List values) {
super(values);
}
public RolloverSpinnerListModel(Object[] values) {
super(values);
}
public Object getNextValue() {
Object returnValue = super.getNextValue();
if (returnValue == null) {
returnValue = getList().get(0);
}
return returnValue;
}
public Object getPreviousValue() {
Object returnValue = super.getPreviousValue();
if (returnValue == null) {
List list = getList();
returnValue = list.get(list.size() - 1);
}
return returnValue;
}}
JSpinner编辑器
对于JSpinner每个可用的模型,一个次要的支持类,JSpinner的一个内部类。然而这个模块可以控制控件是否可选,JSpinner编辑器允许你控制如何显示和编辑每个可选的值。
JSpinner.DefaultEditor类
JSpinner的setEditor()方法允许你将任何Jcomponent作为JSpinner的编辑器,当然你可以那样做,更典型的是,你将用JSpinner.DefaultEditor的子类作运行。以JformattedTextField作为简单的编辑器工作,将提供所有你需要的基本功能。它包括一个基本的构造函数:
在不知道使用的是哪个模型工作的情况下,在这个级别的你可以做的是改变JformattedTextField中的文字显示。更典型的是,你将改变模型编辑器的某些自定义方面。
JSpinner.DateEditor类
DateEditor允许你定制不同的日期显示方式,使用java.text包中SimpleDateFormat类。察看Javadoc了解更多的关于SimpleDateFormat的可用格式模式。如果你不喜欢默认的地显示方式,可以通过给构造函数的第二个参数传递一个新参数来改变显示模式。
public JSpinner.DateEditor(JSpinner spinner)SpinnerModel model = new SpinnerDateModel();
JSpinner spinner = new JSpinner(model);JComponent editor = JSpinner.DateEditor(spinner);
JSpinner.ListEditor类
当使用SpinnerListModel类工作时,ListEditor不支持任何特殊格式。而是提供了前置类型支持。既然模块的所有入口都知道了,编辑器将尝试匹配用户输入的字符。这里只有一个构造函数,但是你可能几乎用不到。
public JSpinner.ListEditor(JSpinner spinner)
在表格8中将看到ListEditor只有一个属性:
JSpinner.NumberEditor类
NumberEditor和DateEditor的工作方式很相似,允许你输入定制的显示模式。代替SimpleDateFormat工作,NumberEditor可以协助java.text包中的DecimalFormat类。就像DateEditor一样,他又两个构造函数:
public JSpinner.NumberEditor(JSpinner spinner)SpinnerModel model = new SpinnerNumberModel(50, 0, 100, .25);JSpinner spinner = new JSpinner(model);JComponent editor = JSpinner.NumberEditor(spinner);spinner.setEditor(editor);public JSpinner.NumberEditor(JSpinner spinner, String decimalFormatPattern)SpinnerModel model = new SpinnerNumberModel(50, 0, 100, .25);JSpinner spinner = new JSpinner(model);JComponent editor = JSpinner.NumberEditor(spinner, "#,##0.###");spinner.setEditor(editor);
第二个构造函数使用默认的字符串格式。如果数字太大,将使用逗号。如果结果是一个完整的数,将不会用十进制显示。
表格9中,显示editor的两个属性。
总结
在这篇文章中,你学习到了Swing中的JSpinner控件。当你要控制某些选择在一定的范围中时,JSpinner可以让你通过翻滚来选择需要的值。你学习到了如何提供这些要选择的值:通过使用SpinnerDateModel和DateEditor,SpinnerListModel和ListEditor,SpinnerNumberModel和NumberEditor来设置日期。