这些 Java 教程是针对 JDK 8 编写的。本页面中描述的示例和实践不利用后续版本引入的改进,并可能使用不再可用的技术。
有关 Java SE 9 及后续版本中更新的语言特性的摘要,请参阅 Java 语言变更。
有关所有 JDK 发行版的新功能、增强功能以及已删除或弃用选项的信息,请参阅 JDK 发行说明。
微调器与组合框和列表相似,允许用户从一系列值中选择。与可编辑的组合框类似,微调器允许用户输入值。与组合框不同,微调器没有下拉列表,可以覆盖其他组件。因为微调器不显示可能的值 - 只有当前值可见 - 所以当可能的值集合非常大时,通常使用微调器代替组合框或列表。然而,只有在可能的值及其顺序明显时才应使用微调器。
微调器是一个复合组件,由三个子组件组成:两个小按钮和一个编辑器。编辑器可以是任何JComponent,但默认情况下,它被实现为包含一个格式化文本字段的面板。微调器的可能值和当前值由其模型管理。
这是一个名为SpinnerDemo的应用程序的图片,其中有三个微调器用于指定日期:
主类的代码可以在SpinnerDemo.java中找到。月份微调器显示用户所在地区的第一个月的名称。此微调器的可能值使用字符串数组指定。年份微调器显示整数范围中的一个值,初始化为当前年份。另一个日期微调器在自定义格式中显示一个Date对象的值(初始为当前日期),仅显示月份和年份。
要创建一个微调器,首先创建它的模型,然后将模型传递给JSpinner构造函数。例如:
String[] monthStrings = getMonthStrings(); //获取月份名称 SpinnerListModel monthModel = new SpinnerListModel(monthStrings); JSpinner spinner = new JSpinner(monthModel);
本节的其余部分涵盖以下主题:
Swing API 提供了三种 Spinner 模型:
SpinnerListModel 是一个模型,其值由对象数组或 List 对象定义。在 SpinnerDemo 示例中,月份 spinner 使用了这个模型,其初始化时使用了从 java.text.DateFormatSymbols 类的 getMonths 方法返回的数组。详细信息请参见 SpinnerDemo.java。
SpinnerNumberModel 支持以 double 对象、int 对象或 Number 对象表示的数字序列。您可以指定允许的最小和最大值,以及每次递增或递减的步长。年份 spinner 使用了这个模型,使用以下代码创建:
SpinnerModel model =
new SpinnerNumberModel(currentYear, // 初始值
currentYear - 100, // 最小值
currentYear + 100, // 最大值
1); // 步长
SpinnerDateModel 支持 Date 对象的序列。您可以指定最小和最大日期,以及要递增或递减的字段(如 Calendar.YEAR)。然而,请注意,某些类型的外观和感觉会忽略指定的字段,而是更改所选的字段。另一个日期 spinner 使用了这个模型,使用以下代码创建:
Date initDate = calendar.getTime();
calendar.add(Calendar.YEAR, -100);
Date earliestDate = calendar.getTime();
calendar.add(Calendar.YEAR, 200);
Date latestDate = calendar.getTime();
model = new SpinnerDateModel(initDate,
earliestDate,
latestDate,
Calendar.YEAR);
当您设置 spinner 的模型时,spinner 的编辑器会自动设置。Swing API 为上述三个模型类提供了相应的编辑器类。这些类 — JSpinner.ListEditor、JSpinner.NumberEditor 和 JSpinner.DateEditor — 都是 JSpinner.DefaultEditor 类的子类,具有可编辑的格式化文本字段。如果使用没有与之关联的编辑器的模型,则编辑器默认为一个具有不可编辑的格式化文本字段的 JSpinner.DefaultEditor 实例。
要更改标准Spinner编辑器中使用的格式,您可以自己创建并设置编辑器。
JSpinner.NumberEditor和JSpinner.DateEditor类具有构造函数,允许您创建以特定方式格式化其数据的编辑器。例如,以下代码设置了Another Date spinner,以便它不使用默认的日期格式(长格式,包括时间),而是以紧凑的方式显示月份和年份。
spinner.setEditor(new JSpinner.DateEditor(spinner, "MM/yyyy"));
您可以通过运行ComboBoxDemo2示例来尝试不同的日期格式。点击“Launch”按钮以使用Java™ Web Start (下载JDK 7或更高版本)。或者,如果要自己编译和运行示例,请参考示例索引。
有关格式字符串的详细信息,请参阅国际化教程的格式化部分。
要在使用默认编辑器时更改格式,可以获取编辑器的格式化文本字段并调用其方法。可以使用JSpinner.DefaultEditor类中定义的getTextField方法调用这些方法。请注意,Swing提供的编辑器不是格式化的文本字段,而是包含格式化文本字段的JPanel实例。以下是获取并调用编辑器的格式化文本字段的示例:
//调整Spinner的格式化文本字段。
ftf = getTextField(spinner);
if (ftf != null ) {
ftf.setColumns(8); //指定比所需宽度更大的宽度
ftf.setHorizontalAlignment(JTextField.RIGHT);
}
...
public JFormattedTextField getTextField(JSpinner spinner) {
JComponent editor = spinner.getEditor();
if (editor instanceof JSpinner.DefaultEditor) {
return ((JSpinner.DefaultEditor)editor).getTextField();
} else {
System.err.println("意外的编辑器类型:"
+ spinner.getEditor().getClass()
+ "不是DefaultEditor的后代");
return null;
}
}
如果现有的Spinner模型或编辑器不满足您的需求,您可以创建自己的模型和编辑器。
创建自定义Spinner模型的最简单方法是创建一个现有AbstractSpinnerModel子类的子类,该子类已经完成了大部分需要的功能。另一种方法是通过扩展AbstractSpinnerModel类来实现自己的类,该类实现了所有Spinner模型所需的事件通知。
下面的SpinnerListModel子类实现了一个循环遍历对象数组的Spinner模型。它还允许您指定一个第二个Spinner模型,在循环重新开始时将进行更新。例如,如果对象数组是一个月份列表,链接的模型可以是显示年份的Spinner。当月份从12月翻转到1月时,年份会增加。同样,当月份从1月翻转回12月时,年份会减少。
public class CyclingSpinnerListModel extends SpinnerListModel {
Object firstValue, lastValue;
SpinnerModel linkedModel = null;
public CyclingSpinnerListModel(Object[] values) {
super(values);
firstValue = values[0];
lastValue = values[values.length - 1];
}
public void setLinkedModel(SpinnerModel linkedModel) {
this.linkedModel = linkedModel;
}
public Object getNextValue() {
Object value = super.getNextValue();
if (value == null) {
value = firstValue;
if (linkedModel != null) {
linkedModel.setValue(linkedModel.getNextValue());
}
}
return value;
}
public Object getPreviousValue() {
Object value = super.getPreviousValue();
if (value == null) {
value = lastValue;
if (linkedModel != null) {
linkedModel.setValue(linkedModel.getPreviousValue());
}
}
return value;
}
}
在SpinnerDemo2示例中,Month spinner使用CyclingSpinnerListModel模型,该示例与SpinnerDemo几乎相同。点击Launch按钮以使用Java™ Web Start运行SpinnerDemo2 (下载JDK 7或更高版本)。或者,要自行编译和运行示例,请参阅示例索引。
如前所述,如果您实现的Spinner模型不是继承自SpinnerListModel、SpinnerNumberModel或SpinnerDateModel,则Spinner的默认编辑器是JSpinner.DefaultEditor的一个不可编辑实例。如您所见,您可以在设置了Spinner模型属性后,通过调用Spinner上的setEditor方法来设置Spinner的编辑器。另一种方法是创建JSpinner类的子类并覆盖其createEditor方法,以便在Spinner模型是某种类型时返回特定类型的编辑器。
至少在理论上,您可以使用任何JComponent实例作为编辑器。可能性包括使用标准组件的子类,如JLabel,或者您从头开始实现的组件,或者JSpinner.DefaultEditor的子类。唯一的要求是编辑器必须更新以反映微调器的值的变化,并且它必须有一个合理的首选大小。编辑器通常还应将其工具提示文本设置为微调器指定的工具提示文本。下一节提供了一个实现编辑器的示例。
您可以通过在微调器或其模型上注册更改侦听器来检测微调器的值是否已更改。下面是实现此类更改侦听器的示例。此示例来自SpinnerDemo3,它基于SpinnerDemo并使用更改侦听器来更改某些文本的颜色,以匹配Another Date微调器的值。点击“启动”按钮以使用Java™ Web Start 运行SpinnerDemo3(下载JDK 7或更高版本)。或者,要自己编译和运行示例,请查阅示例索引。
public class SpinnerDemo3 extends JPanel
implements ChangeListener {
protected Calendar calendar;
protected JSpinner dateSpinner;
...
public SpinnerDemo3() {
...
SpinnerDateModel dateModel = ...;
...
setSeasonalColor(dateModel.getDate()); //初始化颜色
//监听日期微调器的更改。
dateSpinner.addChangeListener(this);
...
}
public void stateChanged(ChangeEvent e) {
SpinnerModel dateModel = dateSpinner.getModel();
if (dateModel instanceof SpinnerDateModel) {
setSeasonalColor(((SpinnerDateModel)dateModel).getDate());
}
}
protected void setSeasonalColor(Date date) {
calendar.setTime(date);
int month = calendar.get(Calendar.MONTH);
JFormattedTextField ftf = getTextField(dateSpinner);
if (ftf == null) return;
//根据北半球季节习惯设置颜色。
switch (month) {
case 2: //三月
case 3: //四月
case 4: //五月
ftf.setForeground(SPRING_COLOR);
break;
...
default: //十二月、一月、二月
ftf.setForeground(WINTER_COLOR);
}
}
...
}
以下示例实现了一个具有更改监听器的编辑器,以便可以反映微调器的当前值。该特定编辑器显示从白色到黑色的任何位置的灰色实心颜色。点击“启动”按钮以使用Java™ Web Start(下载 JDK 7 或更高版本)运行 SpinnerDemo4。或者,要自行编译和运行示例,请参考示例索引。
...//创建组件的位置:
JSpinner spinner = new JSpinner(new GrayModel(170));
spinner.setEditor(new GrayEditor(spinner));
class GrayModel extends SpinnerNumberModel {
...
}
class GrayEditor extends JLabel
implements ChangeListener {
public GrayEditor(JSpinner spinner) {
setOpaque(true);
...
//从模型获取信息。
GrayModel myModel = (GrayModel)(spinner.getModel());
setBackground(myModel.getColor());
spinner.addChangeListener(this);
...
updateToolTipText(spinner);
}
protected void updateToolTipText(JSpinner spinner) {
String toolTipText = spinner.getToolTipText();
if (toolTipText != null) {
//JSpinner 有工具提示文本。使用它。
if (!toolTipText.equals(getToolTipText())) {
setToolTipText(toolTipText);
}
} else {
//定义自己的工具提示文本。
GrayModel myModel = (GrayModel)(spinner.getModel());
int rgb = myModel.getIntValue();
setToolTipText("(" + rgb + "," + rgb + "," + rgb + ")");
}
}
public void stateChanged(ChangeEvent e) {
JSpinner mySpinner = (JSpinner)(e.getSource());
GrayModel myModel = (GrayModel)(mySpinner.getModel());
setBackground(myModel.getColor());
updateToolTipText(mySpinner);
}
}
以下表格列出了一些常用的用于使用微调器的 API。如果您需要直接处理编辑器的格式化文本字段,还应参阅格式化文本字段 API。其他可能使用的方法列在JComponent 类的 API 表中。
| 类或接口 | 目的 |
|---|---|
| JSpinner | 一个单行输入字段,允许用户从有序序列中选择数字或对象值。 |
| SpinnerModel | 所有旋转器模型实现的接口。 |
| AbstractSpinnerModel | 旋转器模型实现通常的超类。 |
| SpinnerListModel | AbstractSpinnerModel的子类,其值由数组或List定义。 |
| SpinnerDateModel | AbstractSpinnerModel的子类,支持Date实例的序列。 |
| SpinnerNumberModel | AbstractSpinnerModel的子类,支持数字的序列。 |
| JSpinner.DefaultEditor | 实现一个不可编辑的组件,用于显示旋转器的值。该类的子类通常更专门(可编辑)。 |
| JSpinner.ListEditor | JSpinner.DefaultEditor的子类,其值由数组或List定义。 |
| JSpinner.DateEditor | JSpinner.DefaultEditor的子类,支持Date实例的序列。 |
| JSpinner.NumberEditor | JSpinner.DefaultEditor的子类,支持数字的序列。 |
| 构造方法或方法 | 目的 |
|---|---|
| JSpinner() JSpinner(SpinnerModel) |
创建一个新的JSpinner。无参数构造方法创建一个带有初始值为0、没有最小或最大限制的整数SpinnerNumberModel的旋转按钮。第二个构造方法的可选参数允许您指定自己的SpinnerModel。 |
| void setValue(java.lang.Object) Object getValue() |
设置或获取当前显示的序列元素。 |
| Object getNextValue() Object getPreviousValue() |
获取getValue方法返回的对象之前或之后的序列中的对象。 |
| SpinnerModel getModel() void setModel(SpinnerModel) |
获取或设置旋转按钮的模型。 |
| JComponent getEditor() void setEditor(JComponent) |
获取或设置旋转按钮的编辑器,通常是JSpinner.DefaultEditor类型的对象。 |
| protected JComponent createEditor(SpinnerModel) | 由JSpinner构造方法调用以创建旋转按钮的编辑器。重写此方法以将编辑器与特定类型的模型关联起来。 |
| 构造函数或方法 | 目的 |
|---|---|
| JSpinner.NumberEditor(JSpinner, String) | 创建一个JSpinner.NumberEditor实例,用于显示和允许编辑指定微调器的数字值。字符串参数指定用于显示数字的格式。有关十进制格式字符串的信息,请参阅API文档中的DecimalFormat。 |
| JSpinner.DateEditor(JSpinner, String) | 创建一个JSpinner.DateEditor实例,用于显示和允许编辑指定微调器的Date值。字符串参数指定用于显示日期的格式。有关日期格式字符串的信息,请参阅API文档中的SimpleDateFormat。 |
| JFormattedTextField getTextField() (定义在 JSpinner.DefaultEditor中) |
获取提供此编辑器主要GUI的格式化文本字段。 |
| 方法 | 目的 |
|---|---|
| void setList(List) List getList() |
设置或获取定义此模型序列的List。 |
| 方法 | 目的 |
|---|---|
| void setValue(Object) Date getDate() Object getValue() |
设置或获取此序列的当前Date。 |
| void setStart(Comparable) Comparable getStart() |
设置或获取此序列的第一个Date。使用null来指定微调器没有下限。 |
| void setEnd(Comparable) Comparable getEnd() |
设置或获取此序列的最后一个Date。使用null来指定微调器没有上限。 |
| void setCalendarField(int) int getCalendarField() |
设置或获取getNextValue和getPreviousValue方法使用的日期值增量的大小。当用户显式增加或减少值时,不使用此属性;相反,递增或递减格式化文本字段的选定部分。指定的参数必须是Calendar中定义的以下常量之一:ERA,YEAR,MONTH,WEEK_OF_YEAR,WEEK_OF_MONTH,DAY_OF_MONTH,DAY_OF_YEAR,DAY_OF_WEEK,DAY_OF_WEEK_IN_MONTH,AM_PM,HOUR_OF_DAY,MINUTE,SECOND,MILLISECOND。 |
| 方法 | 目的 |
|---|---|
| void setValue(Object) Number getNumber() |
设置或获取此序列的当前值。 |
| void setMaximum(Comparable) Comparable getMaximum() |
设置或获取此序列中数字的上限。如果最大值为null,则没有上限。 |
| void setMinimum(Comparable) Comparable getMinimum() |
设置或获取此序列中数字的下限。如果最小值为null,则没有下限。 |
| void setStepSize(Number) Number getStepSize() |
设置或获取getNextValue和getPreviousValue方法使用的增量。 |
此表列出使用Spinner的示例,并指向描述这些示例的位置。
| 示例 | 描述位置 | 备注 |
|---|---|---|
SpinnerDemo |
本节 | 使用所有三种标准的Spinner模型类。包含使用自定义Spinner模型的代码,但默认情况下该代码已关闭。 |
SpinnerDemo2 |
本节 | 一个SpinnerDemo子类,它使用自定义的Spinner模型作为其月份Spinner。 |
SpinnerDemo3 |
本节 | 基于SpinnerDemo,此应用程序演示了如何监听Spinner值的变化。 |
SpinnerDemo4 |
本节 | 实现了一个用于显示灰度阴影的Spinner的自定义模型和自定义编辑器。 |