文档

Java™教程
隐藏目录
如何使用微调器
路径: 使用 Swing 创建 GUI
课程: 使用 Swing 组件
章节: 如何使用各种组件

如何使用 Spinners

微调器与组合框列表相似,允许用户从一系列值中选择。与可编辑的组合框类似,微调器允许用户输入值。与组合框不同,微调器没有下拉列表,可以覆盖其他组件。因为微调器不显示可能的值 - 只有当前值可见 - 所以当可能的值集合非常大时,通常使用微调器代替组合框或列表。然而,只有在可能的值及其顺序明显时才应使用微调器。

微调器是一个复合组件,由三个子组件组成:两个小按钮和一个编辑器。编辑器可以是任何JComponent,但默认情况下,它被实现为包含一个格式化文本字段的面板。微调器的可能值和当前值由其模型管理。

这是一个名为SpinnerDemo的应用程序的图片,其中有三个微调器用于指定日期:

SpinnerDemo显示3种类型的微调器

主类的代码可以在SpinnerDemo.java中找到。月份微调器显示用户所在地区的第一个月的名称。此微调器的可能值使用字符串数组指定。年份微调器显示整数范围中的一个值,初始化为当前年份。另一个日期微调器在自定义格式中显示一个Date对象的值(初始为当前日期),仅显示月份和年份。


试一试: 
  1. 点击启动按钮,使用Java™ Web Start运行SpinnerDemo(下载JDK 7或更高版本)。或者,如果想要自己编译和运行示例,请参考示例索引启动SpinnerDemo应用程序
  2. 使用月份微调器,使用箭头按钮或按键在可能的值之间循环前进和后退。
    注意,最小值是一年的第一个月(例如,一月),最大值是最后一个月(例如,十二月)。具体的值取决于您的区域设置。还请注意,这些值不会循环 - 您不能使用上箭头按钮或按键直接从十二月到一月 - 因为标准微调器模型不支持循环。
  3. 为您的区域设置键入一个有效的月份名称 - 例如,七月。
    注意,微调器会自动完成月份名称。
  4. 移动到年份微调器,尝试键入一个100年前的年份 - 例如,1800 - 然后单击另一个微调器或按Tab键将焦点移出微调器。
    因为此程序将微调器的模型限制为当前年份的100年内的数字,所以1800是无效的。当焦点移出微调器时,显示的文本将更改回最后一个有效值。
  5. 移动到另一个日期微调器,使用箭头按钮或按键更改日期。
    请注意,默认情况下,日期的第一部分 - 在这种情况下,月份的数字 - 会更改。您可以通过单击鼠标或使用箭头键移动到日期的另一部分来更改日期的哪一部分。

要创建一个微调器,首先创建它的模型,然后将模型传递给JSpinner构造函数。例如:

String[] monthStrings = getMonthStrings(); //获取月份名称
SpinnerListModel monthModel = new SpinnerListModel(monthStrings);
JSpinner spinner = new JSpinner(monthModel);

本节的其余部分涵盖以下主题:

 

使用标准的 Spinner 模型和编辑器

Swing API 提供了三种 Spinner 模型:

SpinnerListModel
SpinnerListModel 是一个模型,其值由对象数组或 List 对象定义。在 SpinnerDemo 示例中,月份 spinner 使用了这个模型,其初始化时使用了从 java.text.DateFormatSymbols 类的 getMonths 方法返回的数组。详细信息请参见 SpinnerDemo.java
SpinnerNumberModel
SpinnerNumberModel 支持以 double 对象、int 对象或 Number 对象表示的数字序列。您可以指定允许的最小和最大值,以及每次递增或递减的步长。年份 spinner 使用了这个模型,使用以下代码创建:
SpinnerModel model =
        new SpinnerNumberModel(currentYear, // 初始值
                               currentYear - 100, // 最小值
                               currentYear + 100, // 最大值
                               1);                // 步长
SpinnerDateModel
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.ListEditorJSpinner.NumberEditorJSpinner.DateEditor — 都是 JSpinner.DefaultEditor 类的子类,具有可编辑的格式化文本字段。如果使用没有与之关联的编辑器的模型,则编辑器默认为一个具有不可编辑的格式化文本字段的 JSpinner.DefaultEditor 实例。

指定Spinner格式

要更改标准Spinner编辑器中使用的格式,您可以自己创建并设置编辑器。

JSpinner.NumberEditorJSpinner.DateEditor类具有构造函数,允许您创建以特定方式格式化其数据的编辑器。例如,以下代码设置了Another Date spinner,以便它不使用默认的日期格式(长格式,包括时间),而是以紧凑的方式显示月份和年份。

spinner.setEditor(new JSpinner.DateEditor(spinner, "MM/yyyy"));

注意: 

您可以通过运行ComboBoxDemo2示例来尝试不同的日期格式。点击“Launch”按钮以使用Java™ Web Start 下载JDK 7或更高版本)。或者,如果要自己编译和运行示例,请参考示例索引

启动ComboBoxDemo2应用程序

有关格式字符串的详细信息,请参阅国际化教程的格式化部分。


要在使用默认编辑器时更改格式,可以获取编辑器的格式化文本字段并调用其方法。可以使用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模型或编辑器不满足您的需求,您可以创建自己的模型和编辑器。

创建自定义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或更高版本)。或者,要自行编译和运行示例,请参阅示例索引

启动SpinnerDemo2应用程序

如前所述,如果您实现的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或更高版本)。或者,要自己编译和运行示例,请查阅示例索引

启动SpinnerDemo3应用程序
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。或者,要自行编译和运行示例,请参考示例索引

启动 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。如果您需要直接处理编辑器的格式化文本字段,还应参阅格式化文本字段 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()
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的格式化文本字段。
SpinnerListModel方法
方法 目的
void setList(List)
List getList()
设置或获取定义此模型序列的List
SpinnerDateModel方法
方法 目的
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()
设置或获取getNextValuegetPreviousValue方法使用的日期值增量的大小。当用户显式增加或减少值时,不使用此属性;相反,递增或递减格式化文本字段的选定部分。指定的参数必须是Calendar中定义的以下常量之一:ERAYEARMONTHWEEK_OF_YEARWEEK_OF_MONTHDAY_OF_MONTHDAY_OF_YEARDAY_OF_WEEKDAY_OF_WEEK_IN_MONTHAM_PMHOUR_OF_DAYMINUTESECONDMILLISECOND
SpinnerNumberModel方法
方法 目的
void setValue(Object)
Number getNumber()
设置或获取此序列的当前值。
void setMaximum(Comparable)
Comparable getMaximum()
设置或获取此序列中数字的上限。如果最大值为null,则没有上限。
void setMinimum(Comparable)
Comparable getMinimum()
设置或获取此序列中数字的下限。如果最小值为null,则没有下限。
void setStepSize(Number)
Number getStepSize()
设置或获取getNextValuegetPreviousValue方法使用的增量。

使用Spinner的示例

此表列出使用Spinner的示例,并指向描述这些示例的位置。

示例 描述位置 备注
SpinnerDemo 本节 使用所有三种标准的Spinner模型类。包含使用自定义Spinner模型的代码,但默认情况下该代码已关闭。
SpinnerDemo2 本节 一个SpinnerDemo子类,它使用自定义的Spinner模型作为其月份Spinner。
SpinnerDemo3 本节 基于SpinnerDemo,此应用程序演示了如何监听Spinner值的变化。
SpinnerDemo4 本节 实现了一个用于显示灰度阴影的Spinner的自定义模型和自定义编辑器。

上一页: 如何使用滑块
下一页: 如何使用分割窗格