12.9.2 不强制存储列表项的ListModel和ComboBoxModel
正如前面提到的,Swing
的绝大部分组件都采用了MVC
的设计模式,其中JList
和JComboBox
都只负责组件的外观显示,而组件底层的状态数据维护则由对应的Model
负责。
JList
对应的Model
是ListModel
接口,
JCombobox
对应的Model
是ComboBoxModel
接口.
这两个接口负责维护JList
和JCombobox
组件里的列表项。
ListModel接口
ListModel
接口的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12
| package javax.swing; import javax.swing.event.ListDataListener; public interface ListModel<E>{ int getSize(); E getElementAt(int index); void addListDataListener(ListDataListener l); void removeListDataListener(ListDataListener l); }
|
从上面接口来看,这个ListModel
不管JList
里的所有列表项的存储形式,它甚至不强制存储所有的列表项,只要ListModel
的实现类提供了getSize()
和getElementAt()
两个方法,JList
就可以根据该ListModel
对象来生成列表框
ComboBoxModel接口
ComboBoxModel
继承了ListModel
,它添加了“选择项”的概念,选择项代表JComboBox
显示区域内可见的列表项。ComboBoxModel
为“选择项”提供了两个方法,下面是ComboModel
接口的代码。
1 2 3 4 5 6 7
| package javax.swing; public interface ComboBoxModel<E> extends ListModel<E> { void setSelectedItem(Object anItem); Object getSelectedItem(); }
|
创建ListModel实现类
因为ListModel
不强制保存所有的列表项,因此可以为它创建一个实现类:NumberListModel
,这个实现类只需要传入数字上限、数字下限和步长,程序就可以自动为之实现上面的getsize()
方法和getElementAt()
方法,从而允许直接使用一个数字范围来创建JList
对象。
实现getSize()
方法
实现getSize()
方法的代码如下
1 2 3
| public int getsize (){ return (int) Math.floor(end.subtract(start).divide(step).doublevalue())+ 1; }
|
用“**(上限-下限)÷步长+1**”即得到该ListModel
中包含的列表项的个数
程序使用BigDecimal
变量来保存上限、下限和步长,而不是直接使用double
变量来保存这三个属性,主要是为了实现对数值的精确计算,所以上面程序中的end
、start
和step
都是BigDecimal
类型的变量。
实现getElementAt方法
实现getElementAt()
方法也很简单,“下限+步长×索引”就是指定索引处的元素,该方法的具体实现请参考ListModelTest.java
程序 创建ListModel的实现类 创建ComboBoxModel的实现类
下面程序为ListModel
提供了NumberListModel
实现类,并为ComboBoxModel
提供了NumberComboBoxModel
实现类,这两个实现类允许程序使用数值范围来创建JList
和JComboBox
对象。
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 97 98 99 100 101 102 103 104 105
| import java.math.BigDecimal; import java.util.*; import java.awt.BorderLayout; import javax.swing.*;
public class ListModelTest { private JFrame mainWin = new JFrame("测试ListModel"); private JList<BigDecimal> numScopeList = new JList<>(new NumberListModel(1, 21, 2)); private JComboBox<BigDecimal> numScopeSelector = new JComboBox<>(new NumberComboBoxModel(0.1, 1.2, 0.1)); private JTextField showVal = new JTextField(10);
public void init() { numScopeList.setVisibleRowCount(4); numScopeList.setSelectionInterval(2, 4); numScopeList.setFixedCellHeight(30); numScopeList.setFixedCellWidth(90); numScopeList.addListSelectionListener(e -> { List<BigDecimal> nums = numScopeList.getSelectedValuesList(); showVal.setText(""); for (BigDecimal num : nums) { showVal.setText(showVal.getText() + num.toString() + ", "); } }); numScopeSelector.setMaximumRowCount(5); Box box = new Box(BoxLayout.X_AXIS); box.add(new JScrollPane(numScopeList)); JPanel p = new JPanel(); p.add(numScopeSelector); box.add(p); numScopeSelector.addItemListener(e -> { Object num = numScopeSelector.getSelectedItem(); showVal.setText(num.toString()); }); JPanel bottom = new JPanel(); bottom.add(new JLabel("您选择的值是:")); bottom.add(showVal); mainWin.add(box); mainWin.add(bottom, BorderLayout.SOUTH); mainWin.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); mainWin.pack(); mainWin.setVisible(true); }
public static void main(String[] args) { new ListModelTest().init(); } }
class NumberListModel extends AbstractListModel<BigDecimal> { protected BigDecimal start; protected BigDecimal end; protected BigDecimal step;
public NumberListModel(double start, double end, double step) { this.start = BigDecimal.valueOf(start); this.end = BigDecimal.valueOf(end); this.step = BigDecimal.valueOf(step); }
public int getSize() { return (int) Math.floor(end.subtract(start).divide(step).doubleValue()) + 1; }
public BigDecimal getElementAt(int index) { return BigDecimal.valueOf(index).multiply(step).add(start); } }
class NumberComboBoxModel extends NumberListModel implements ComboBoxModel<BigDecimal> { private int selectId = 0;
public NumberComboBoxModel(double start, double end, double step) { super(start, end, step); }
public void setSelectedItem(Object anItem) { if (anItem instanceof BigDecimal) { BigDecimal target = (BigDecimal) anItem; selectId = target.subtract(super.start).divide(step).intValue(); } }
public BigDecimal getSelectedItem() { return BigDecimal.valueOf(selectId).multiply(step).add(start); } }
|
上面程序中分别使用NumberListModel
和NumberComboBoxModel
创建了一个JList
和JComboBox
对象,创建这两个列表框时无须指定每个列表项,只需给出数值的上限、下限和步长即可。运行上面程序,会看到如图12.35所示的窗口。