如何使用微调器

微调器与combo boxeslists相似,它们允许用户从一系列值中进行选择。像可编辑的组合框一样,微调器允许用户键入一个值。与组合框不同,微调框没有下拉列表,该下拉列表可以掩盖其他组件。由于微调框不显示可能的值-仅显示当前值-当可能值的集合非常大时,通常使用它们而不是组合框或列表。但是,仅当可能的值及其 Sequences 很明显时才应使用微调器。

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

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

SpinnerDemo 显示 3 种微调器

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

Try this:

  • 单击启动按钮以使用Java™Web 开始(下载 JDK 7 或更高版本)运行 SpinnerDemo。另外,要自己编译和运行示例,请查阅example index

  • 使用“月份”微调器,使用箭头按钮或键在可能的值之间来回循环。
    请注意,最小值是一年中的第一个月(例如,一月),最大值是最后一个月(例如,十二月)。确切的值取决于您的区域设置。还要注意,这些值不会循环-您不能使用向上箭头按钮或向上键直接从 12 月转到 1 月-因为标准微调器型号不支持循环。

  • Importing 您所在区域的有效月份名称,例如 July。
    请注意,微调器会自动完成月份名称。

  • 转到“年份”微调器,try键入 100 年前的一年(例如 1800),然后单击另一个微调器或按 Tab 键将焦点移出微调器。
    因为此程序将微调器的模型限制为当年 100 年内的数字,所以 1800 无效。当焦点移出微调器时,显示的文本将变回最后一个有效值。

  • 移至另一个日期微调器,使用箭头按钮或键更改日期。
    请注意,默认情况下,日期的第一部分(在这种情况下为月数)会更改。您可以通过单击鼠标或使用箭头键移动日期的另一部分来更改日期的哪一部分。

要创建微调器,请首先创建其模型,然后将模型传递到JSpinner构造函数中。例如:

String[] monthStrings = getMonthStrings(); //get month names
SpinnerListModel monthModel = new SpinnerListModel(monthStrings);
JSpinner spinner = new JSpinner(monthModel);

本节的其余部分包括以下主题:

使用标准微调器模型和编辑器

Swing API 提供了三种微调器模型:

  • SpinnerListModel

    • SpinnerListModel是一个模型,其值由对象数组或List对象定义。 SpinnerDemo示例中的 Month 微调器使用此模型,并使用从java.text.DateFormatSymbols类的getMonths方法返回的值派生的数组进行初始化。有关详情,请参见SpinnerDemo.java
  • SpinnerNumberModel

    • SpinnerNumberModel支持数字序列,可以表示为double对象,int对象或Number对象。您可以指定最小和最大允许值,以及“步 Long”(每次增加或减少的数量)。 Year 微调器使用此模型,该模型使用以下代码创建:
SpinnerModel model =
        new SpinnerNumberModel(currentYear, //initial value
                               currentYear - 100, //min
                               currentYear + 100, //max
                               1);                //step
  • SpinnerDateModel

    • SpinnerDateModel支持Date对象的序列。您可以指定最小和最大日期,以及要递增或递减的字段(例如Calendar.YEAR)。但是请注意,某些类型的外观会忽略指定的字段,而是更改显示为选中状态的字段。 Another Date 微调器使用此模型,该模型使用以下代码创建:
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);

设置微调器的模型时,将自动设置微调器的编辑器。 Swing API 提供了一个与上面列出的三个模型类相对应的编辑器类。这些类JSpinner.ListEditorJSpinner.NumberEditorJSpinner.DateEditorJSpinner.DefaultEditor类的所有子类,这些子类具有可编辑的格式化文本字段。如果您使用的模型没有与之关联的编辑器,则默认情况下,该编辑器是JSpinner.DefaultEditor实例,该实例具有不可编辑的格式化文本字段。

指定微调框格式

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

JSpinner.NumberEditorJSpinner.DateEditor类具有构造函数,可让您创建以特定方式格式化其数据的编辑器。例如,以下代码设置了“另一个日期”微调器,以便代替使用 Long 且包含时间的默认日期格式,它以紧凑的方式仅显示一个月和一年。

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

Note:

您可以通过运行ComboBoxDemo2示例来使用日期格式。单击启动按钮以使用Java™Web 开始(下载 JDK 7 或更高版本)运行 ComboBoxDemo2.另外,要自己编译和运行示例,请查阅example index

有关格式字符串 的信息,请参阅国际化跟踪的Formatting类。

要在使用默认编辑器时更改格式,可以获取编辑器的格式化文本字段并在其上调用方法。您可以使用JSpinner.DefaultEditor类中定义的getTextField方法来调用这些方法。请注意,Swing 提供的编辑器不是格式化的文本字段。相反,它们是包含格式化文本字段的JPanel实例。这是在编辑器的格式化文本字段上获取和调用方法的示例:

//Tweak the spinner's formatted text field.
ftf = getTextField(spinner);
if (ftf != null ) {
    ftf.setColumns(8); //specify more width than we need
    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("Unexpected editor type: "
                           + spinner.getEditor().getClass()
                           + " isn't a descendant of DefaultEditor");
        return null;
    }
}

创建自定义微调器模型和编辑器

如果现有的微调器模型或编辑器无法满足您的需求,则可以创建自己的微调器模型或编辑器。

创建自定义微调器模型的最简单方法是创建现有AbstractSpinnerModel子类的子类,该子类已经可以满足您的大部分需求。一种替代方法是通过扩展AbstractSpinnerModel类来实现自己的类,该类实现所有微调器模型所需的事件通知。

SpinnerListModel的以下子类实现了微调器模型,该模型在对象数组中循环。它还允许您指定第二个微调器模型,该模型将在周期再次开始时进行更新。例如,如果对象数组是月份列表,则链接的模型可能适用于显示年份的微调框。当月份从 12 月翻转到 1 月时,年份会增加。同样,当月份从一月退回到十二月时,年份会递减。

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;
    }
}

CyclingSpinnerListModel模型用于SpinnerDemo2示例中的 Month 微调器,该示例几乎与SpinnerDemo相同。单击启动按钮以使用Java™Web 开始(下载 JDK 7 或更高版本)运行 SpinnerDemo2.另外,要自己编译和运行示例,请查阅example index

如前所述,如果实现的旋转器模型不是SpinnerListModelSpinnerNumberModelSpinnerDateModel的子代,则旋转器的默认编辑器是JSpinner.DefaultEditor的不可编辑实例。如您所见,可以在设置了微调器的 model 属性之后,通过调用微调器上的setEditor方法来设置微调器的编辑器。使用setEditor的替代方法是创建JSpinner类的子类并覆盖其createEditor方法,以便在微调器模型为特定类型时返回特定类型的编辑器。

至少从理论上讲,您可以使用任何JComponent实例作为编辑器。可能包括使用诸如JLabel之类的标准组件的子类,或者从头开始实现的组件,或JSpinner.DefaultEditor的子类。唯一的要求是必须更新编辑器以反映微调器值的变化,并且编辑器必须具有合理的首选大小。编辑器通常还应该将其工具提示文本设置为为微调器指定的任何工具提示文本。下一节提供了一个实现编辑器的示例。

检测微调器值更改

通过在微调器或其模型上注册更改侦听器,可以检测到微调器的值已更改。这是实现此类更改侦听器的示例。本示例来自SpinnerDemo3,它基于SpinnerDemo并使用更改侦听器更改某些文本的颜色以匹配“另一个日期”微调器的值。单击启动按钮以使用Java™Web 开始(下载 JDK 7 或更高版本)运行 SpinnerDemo3.另外,要自己编译和运行示例,请查阅example index

public class SpinnerDemo3 extends JPanel
                          implements ChangeListener {
    protected Calendar calendar;
    protected JSpinner dateSpinner;
    ...
    public SpinnerDemo3() {
        ...
        SpinnerDateModel dateModel = ...;
        ...
        setSeasonalColor(dateModel.getDate()); //initialize color

        //Listen for changes on the date spinner.
        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;

        //Set the color to match northern hemisphere seasonal conventions.
        switch (month) {
            case 2:  //March
            case 3:  //April
            case 4:  //May
                     ftf.setForeground(SPRING_COLOR);
                     break;
            ...
            default: //December, January, February
                     ftf.setForeground(WINTER_COLOR);
        }
    }
    ...
}

下面的示例实现了一个具有更改侦听器的编辑器,以便它可以反映微调器的当前值。该特定的编辑器显示纯色的灰色,范围从白色到黑色。单击启动按钮以使用Java™Web 开始(下载 JDK 7 或更高版本)运行 SpinnerDemo4.另外,要自己编译和运行示例,请查阅example index

...//Where the components are created:
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);
        ...
        //Get info from the model.
        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 has tool tip text.  Use it.
            if (!toolTipText.equals(getToolTipText())) {
                setToolTipText(toolTipText);
            }
        } else {
            //Define our own tool tip text.
            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);
    }
}

Spinner API

下表列出了一些用于微调器的常用 API。如果您需要直接处理编辑器的格式化文本字段,则还应该看到FormattedTextField APIJComponent 类中的 API 表中列出了您可能使用的其他方法。

与微调器相关的类

类或interfacePurpose
JSpinner单行 Importing 字段,允许用户从有序序列中选择数字或对象值。
SpinnerModel由所有微调器模型实现的interface。
AbstractSpinnerModel微调器模型实现的通常超类。
SpinnerListModelAbstractSpinnerModel的子类,其值由数组或List定义。
SpinnerDateModelAbstractSpinnerModel的子类,支持Date实例的序列。
SpinnerNumberModelAbstractSpinnerModel的子类,支持数字序列。
JSpinner.DefaultEditor实现一个不可编辑的组件,该组件显示微调框的值。此类的子类通常更专门(并且可编辑)。
JSpinner.ListEditorJSpinner.DefaultEditor的子类,其值由数组或List定义。
JSpinner.DateEditorJSpinner.DefaultEditor的子类,支持Date实例的序列。
JSpinner.NumberEditorJSpinner.DefaultEditor的子类,支持数字序列。

有用的 JSpinner 构造函数和方法

构造函数或方法Purpose
JSpinner()

JSpinner(SpinnerModel)
创建一个新的JSpinner。无参数构造函数创建一个带有整数SpinnerNumberModel的微调器,该整数SpinnerNumberModel的初始值为 0,没有最小或最大限制。第二个构造函数上的可选参数允许您指定自己的SpinnerModel
void setValue(java.lang.Object)
Object getValue()
设置或获取序列中当前显示的元素。
Object getNextValue()
Object getPreviousValue()
按照getValue方法返回的对象之前或之后的 Sequences 获取对象。
SpinnerModel getModel()
void setModel(SpinnerModel)
获取或设置微调器的模型。
JComponent getEditor()
void setEditor(JComponent)
获取或设置微调框的编辑器,通常是JSpinner.DefaultEditor类型的对象。
受保护的 JComponent createEditor(SpinnerModel)JSpinner构造函数调用以创建微调框的编辑器。重写此方法可将编辑器与特定类型的模型相关联。

有用的编辑器构造函数和方法

构造函数或方法Purpose
JSpinner.NumberEditor(JSpinner, String)创建一个JSpinner.NumberEditor实例,该实例显示并允许编辑指定微调器的数字值。字符串 参数指定用于显示数字的格式。有关十进制格式字符串 的信息,请参见DecimalFormat的 API 文档。
JSpinner.DateEditor(JSpinner, String)创建一个JSpinner.DateEditor实例,该实例显示并允许编辑指定微调器的Date值。字符串 参数指定用于显示日期的格式。有关日期格式字符串 的信息,请参见SimpleDateFormat的 API 文档。
JFormattedTextField getTextField()

(在JSpinner.DefaultEditor中定义)
获取提供此编辑器主 GUI 的格式化文本字段。

SpinnerListModel Methods

MethodPurpose
void setList(List)

List getList()
设置或获取List,该List定义此模型的 Sequences。

SpinnerDateModel Methods

MethodPurpose
void setValue(Object)

Date getDate()
Object getValue()
为此序列设置或获取当前Date
void setStart(Comparable)
Comparable getStart()
按此 Sequences 设置或获取第一个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 Methods

MethodPurpose
void setValue(Object)

Number getNumber()
设置或获取此序列的当前值。
void setMaximum(Comparable)
Comparable getMaximum()
设置或获取此序列中数字的上限。如果最大值为null,则没有上限。
void setMinimum(Comparable)
Comparable getMinimum()
设置或获取此序列中数字的下限。如果最小值为null,则没有下限。
void setStepSize(Number)
Number getStepSize()
设置或获取getNextValuegetPreviousValue方法使用的增量。

使用微调框的示例

下表列出了使用微调器的示例,并指向描述这些示例的位置。

ExampleWhere DescribedNotes
SpinnerDemoThis section使用所有三个标准微调器模型类。包含使用自定义微调器模型的代码,但是默认情况下该代码处于关闭状态。
SpinnerDemo2This sectionSpinnerDemo子类,其“月”微调器使用自定义微调器模型。
SpinnerDemo3This section该应用程序基于 SpinnerDemo,显示了如何侦听微调器值的变化。
SpinnerDemo4This section为显示灰色 shade 的微调器实现自定义模型和自定义编辑器。