12.8 使用JSpinner和SpinnerModel刨建微调控制器

12.8 使用JSpinner和SpinnerModel刨建微调控制器

JSpinner组件是一个带有两个小箭头的文本框,这个文本框只能接收满足要求的数据,用户既可以通过两个小箭头调整该微调控制器的值,也可以直接在文本框内输入内容作为该微调控制器的值。当用户在该文本框内输入时,如果输入的内容不满足要求,系统将会拒绝用户输入。典型的JSpinner组件如图12.32所示。

JSpinner组件常常需要和SpinnerModel结合使用,其中JSpinner组件控制该组件的外观表现,而SpinnerModel则控制该组件内部的状态数据。

JSpinner组件的值

JSpinner组件的值可以是数值、日期和List中的值,Swing为这三种类型的值提供了SpinnerNumberModelSpinnerDateModelSpinnerListModel三个SpinnerModel实现类;

自定义SpinnerModel实现类

除此之外,JSpinner组件的值还可以是任意序列,只要这个序列可以通过previous()方法和next()方法获取值即可。在这种情况下,用户必须自行提供SpinnerModel实现类。

使用JSpinner组件非常简单,JSpinner提供了如下两个构造器。

构造器 描述
JSpinner() 创建一个默认的微调控制器。创建的默认微调控制器只接收整数值,初始值是0,最大值和最小值没有任何限制制。每单击向下箭头或者向上箭头一次,该组件里的值分别减1或加1。
JSpinner(SpinnerModel model) 使用指定的SpinnerModel来创建微调控制器。

Swing提供的三个SpinnerModel的功能

使用JSpinner关键在于使用它对应的三个SpinnerModel,下面依次介绍这三个SpinnerModel

  • SpinnerNumberModel:这是最简单的SpinnerModel,创建该SpinnerModel时可以指定4个参数:最大值、最小值、初始值、步长,其中步长控制单击上、下箭头时相邻两个值之间的差。这4个参数既可以是整数,也可以是浮点数。
  • SpinnerDateModel:创建该SpinnerModel时可以指定4个参数:起始时间、结束时间、初始时间和时间差,其中时间差控制单击上、下箭头时相邻两个时间之间的差值。
  • SpinnerListModel:创建该SpinnerModel只需要传入一个List或者一个数组作为序列值即可。该List的集合元素和数组元素可以是任意类型的对象,但由于JSpinner组件的文本框只能显示字符串,所以JSpinner显示每个对象的toString()方法的返回值。

从图12.32中可以看出,JSpinner创建的微调控制器和ComboBox有点像(由SwingJComboBox提供,ComboBox既允许通过下拉列表框进行选择,也允许直接输入)

JSpinner和JComboBox的区别

Combobox可以产生一个下拉列表框供用户选择,而JSpinner组件只能通过上、下箭头逐项选择。
使用ComboBox通常必须明确指定下拉列表框中毎一项的值,但使用JSpinner则只需给定一个范围,并指定步长即可;当然,使用JSpinner也可以明确给出每项的值(就是对应使用SpinnerListModel)

为了控制JSpinner中值的显示格式,JSpinner还提供了一个setEditor()方法。Swing提供了如下3个特殊的Editor来控制值的显示格式:

  • JSpinner.DateEditor:控制JSpinner中日期值的显示格式。
  • JSpinner.ListEditor:控制JSpinnerList项的显示格式
  • JSpinner.NumberEditor:控制JSpinner中数值的显示格式

程序 使用JSpinner

下面程序示范了几种使用JSpinner的情形

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import java.awt.*;
import javax.swing.*;
import java.util.Date;
import java.util.ArrayList;
import java.util.Calendar;

public class JSpinnerTest {
final int SPINNER_NUM = 6;
JFrame mainWin = new JFrame("微调控制器示范");
Box spinnerBox = new Box(BoxLayout.Y_AXIS);
JSpinner[] spinners = new JSpinner[SPINNER_NUM];
JLabel[] valLabels = new JLabel[SPINNER_NUM];
JButton okBn = new JButton("确定");

public void init() {
for (int i = 0; i < SPINNER_NUM; i++) {
valLabels[i] = new JLabel();
}
// -----------普通JSpinner-----------
spinners[0] = new JSpinner();
addSpinner(spinners[0], "普通", valLabels[0]);
// -----------指定最小值、最大值、步长的JSpinner-----------
// 创建一个SpinnerNumberModel对象,指定最小值、最大值和步长
SpinnerNumberModel numModel = new SpinnerNumberModel(3.4, -1.1, 4.3, 0.1);
spinners[1] = new JSpinner(numModel);
addSpinner(spinners[1], "数值范围", valLabels[1]);
// -----------使用SpinnerListModel的JSpinner------------
String[] books = new String[] { "轻量级Java EE企业应用实战", "疯狂Java讲义", "疯狂Ajax讲义" };
// 使用字符串数组创建SpinnerListModel对象
SpinnerListModel bookModel = new SpinnerListModel(books);
// 使用SpinnerListModel对象创建JSpinner对象
spinners[2] = new JSpinner(bookModel);
addSpinner(spinners[2], "字符串序列值", valLabels[2]);
// -----------使用序列值是ImageIcon的JSpinner------------
ArrayList<ImageIcon> icons = new ArrayList<>();
icons.add(new ImageIcon("a.gif"));
icons.add(new ImageIcon("b.gif"));
// 使用ImageIcon数组创建SpinnerListModel对象
SpinnerListModel iconModel = new SpinnerListModel(icons);
// 使用SpinnerListModel对象创建JSpinner对象
spinners[3] = new JSpinner(iconModel);
addSpinner(spinners[3], "图标序列值", valLabels[3]);
// -----------使用SpinnerDateModel的JSpinner------------
// 分别获取起始时间、结束时间、初时时间
Calendar cal = Calendar.getInstance();
Date init = cal.getTime();
cal.add(Calendar.DAY_OF_MONTH, -3);
Date start = cal.getTime();
cal.add(Calendar.DAY_OF_MONTH, 8);
Date end = cal.getTime();
// 创建一个SpinnerDateModel对象,指定最小时间、最大时间和初始时间
SpinnerDateModel dateModel = new SpinnerDateModel(init, start, end, Calendar.HOUR_OF_DAY);
// 以SpinnerDateModel对象创建JSpinner
spinners[4] = new JSpinner(dateModel);
addSpinner(spinners[4], "时间范围", valLabels[4]);
// -----------使用DateEditor来格式化JSpinner------------
dateModel = new SpinnerDateModel();
spinners[5] = new JSpinner(dateModel);
// 创建一个JSpinner.DateEditor对象,用于对指定Spinner进行格式化
JSpinner.DateEditor editor = new JSpinner.DateEditor(spinners[5], "公元yyyy年MM月dd日 HH时");
// 设置使用JSpinner.DateEditor对象进行格式化
spinners[5].setEditor(editor);
addSpinner(spinners[5], "使用DateEditor", valLabels[5]);
// 为“确定”按钮添加一个事件监听器
okBn.addActionListener(evt -> {
// 取出每个微调控制器的值,并将该值用后面的Label标签显示出来。
for (int i = 0; i < SPINNER_NUM; i++) {
// 将微调控制器的值通过指定的JLabel显示出来
valLabels[i].setText(spinners[i].getValue().toString());
}
});
JPanel bnPanel = new JPanel();
bnPanel.add(okBn);
mainWin.add(spinnerBox, BorderLayout.CENTER);
mainWin.add(bnPanel, BorderLayout.SOUTH);
mainWin.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainWin.pack();
mainWin.setVisible(true);
}

// 定义一个方法,用于将滑动条添加到容器中
public void addSpinner(JSpinner spinner, String description, JLabel valLabel) {
Box box = new Box(BoxLayout.X_AXIS);
JLabel desc = new JLabel(description + ":");
desc.setPreferredSize(new Dimension(100, 30));
box.add(desc);
box.add(spinner);
valLabel.setPreferredSize(new Dimension(180, 30));
box.add(valLabel);
spinnerBox.add(box);
}

public static void main(String[] args) {
new JSpinnerTest().init();
}
}

上面程序创建了6个JSpinner对象,并将它们添加到窗口中显示出来

  • 第一个JSpinner组件是一个默认的微调控制器,其初始值是0,步长是1,只能接收整数值。
  • 第二个JSpinner通过SpinnerNumberModel来创建,指定了JSpinner的最小值为-1.1、最大值为4.3、初始值为34、步长为0.1,所以用户单击该微调控制器的上、下箭头时,微调控制器的值之间的差值是0.1,并只能处于-1.1~4.3之间
  • 第三个JSpinner通过SpinnerListModel来创建的,创建SpinnerListModel对象时指定字符串数组作为多个序列值,所以当用户单击该微调控制器的上、下箭头时,微调控制器的值总是在该字符串数组之间选择。
  • 第四个JSpinner也是通过SpinnerListmodel来创建的,虽然传给SpinnerListModel对象的构造参数是集合元素为ImageIconList对象,但JSpinner只能显示字符串内容,所以它会把每个Imagelcon对象的toString方法返回值当成微调控制器的多个序列值。
  • 第五个JSpinner通过SpinnerDateModel来创建,而且指定了最小时间、最大时间和初始时间,所以用户单击该微调控制器的上、下箭头时,微调控制器里的时间只能处于指定时间范围之间。这里需要注意的是,SpinnerDateModel的第4个参数没有太大的作用,它不能控制两个相邻时间之间的差。当用户在JSpinner组件内选中该时间的指定时间域时,例如年份,则两个相邻时间的时间差就是1年。
  • 第六个JSpinner使用SPinner.DateEditor来控制时间微调控制器里日期、时间的显示格式,创建JSpinner.DateEditor对象时需要传入一个日期时间格式字符串(dateFormatPattern),该参数用于控制日期、时间的显示格式,关于这个格式字符串的定义方式可以参考SimpleDateFormat类的介绍。本例程序中使用”公元yyyy年MM月dd日HH时”作为格式字符串。

运行上面程序,会看到如图12.33所示的窗口。

图12.33

程序中还提供了一个“确定”按钮,当单击该按钮时,系统会把每个微调控制器的值通过对应的JLabel标签显示出来,如图12.33所示。