我们仅举CheckBoxCell这个类的例子简单说明一下如何使用TableCellSupport进行自定义扩展。CheckBoxCell就是定义一个JCheckBox为编辑器和渲染器的实现:
public class CheckBoxCell extends TableCellSupport{
public CheckBoxCell(JCheckBox cb){
super(cb);
cb.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
//被选中时需要触发编辑停止事件,一般直接调用父类的stopCellEditing即可,那儿已经负责了有效性检查,事件触发。
stopCellEditing();
}
});
}
protected void setValueTo(JCheckBox component, Object value) {
//认为value值是Boolean类型的,注意空值的处理
component.setSelected(value==null?false:((Boolean)value).booleanValue());
}
protected Object getValueFrom(JCheckBox component) {
//返回当前选中状态的布尔值,用Boolean封装
return new Boolean(component.isSelected());
}
}
BeanProperty是一个简单的封装类,目的是打包一个属性的描述,包括它的名称、值、渲染器和编辑器。它的代码如下:
public class BeanProperty {
//属性显示名称,属性表格第一列显示名
private String displayName;
//属性的值,属性表格第二列的值
private Object value;
//渲染和编辑属性值用的渲染编辑器(Support类)
private TableCellSupport support;
public BeanProperty(String displayName, TableCellSupport support) {
this.displayName=displayName;
this.support=support;
}
public String getDisplayName() {...}
public Object getValue() {...}
public void setValue(Object value) {...}
public TableCellSupport getSupport() {...}
}
BeanPropertyTable继承了JTable类,它覆盖了JTable的两方法getCellRenderer和getCellEditor来提供自定义的渲染器和编辑器,并使用基于BeanProperty数组的数据模型BeanModel:
public class BeanPropertyTable extends JTable {
...
private ArrayList properties;
public BeanPropertyTable() {
properties=new ArrayList();
...
}
public void setProperties(ArrayList properties){
if(properties!=null){
this.properties=properties;
setModel(new BeanModel());
}
}
//自定义的TableModel
private class BeanModel extends AbstractTableModel{
public int getRowCount() {
//属性表的行数
return properties.size();
}
public int getColumnCount() {
//属性表的列数
return 2;
}
public String getColumnName(int columnIndex) {
//属性表的列名:property, value
return columnIndex==0?"property":"value";
}
public boolean isCellEditable(int rowIndex, int columnIndex) {
//第二列属性值可编辑
return columnIndex==1;
}
public Object getValueAt(int rowIndex, int columnIndex) {
//获取值,第一列用属性显示名,第二列用属性值
BeanProperty property=properties.get(rowIndex);
return columnIndex==0?property.getDisplayName():property.getValue();
}
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
if(columnIndex==1)//只有第二列可编辑,设置第二列到属性值
properties.get(rowIndex).setValue(aValue);
}
}
//覆盖父类的getCellRenderer提供个性化的渲染器
public TableCellRenderer getCellRenderer(int row, int column) {
if(column==0)//第一列使用继承的渲染器
return super.getCellRenderer(row, column);
else//第二列使用属性对象自己提供的渲染器,注意Support类实现了TableCellRenderer
return properties.get(row).getSupport();
}
//覆盖父类的getCellEditor提供个性化的编辑器
public TableCellEditor getCellEditor(int row, int column) {
if(column==0)//第一列使用继承的编辑器
return super.getCellEditor(row,column);
else//第二列使用属性对象自己提供的编辑器,注意Support类实现了TableCellEditor
return properties.get(row).getSupport();
}
}
最后看看主类PropertyDemo.java的实现,非常简单,非常直接:
public class PropertyDemo extends JFrame{
public PropertyDemo() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
BeanPropertyTable table=new BeanPropertyTable();
ArrayList props=new ArrayList();
for(int i=0;i<2;i++){
//添加一个textfield属性,其渲染编辑组件是JTextField
props.add(new BeanProperty("textfield"+i,new TextFieldCell(new JTextField())));
//添加一个combo属性,其渲染编辑组件是JComboBox
JComboBox cb=new JComboBox();
cb.addItem("true");
cb.addItem("false");
props.add(new BeanProperty("combobox"+i,new ComboBoxCell(cb)));
//添加一个checkbox属性,其渲染编辑组件是 JCheckBox
props.add(new BeanProperty("checkbox"+i,new CheckBoxCell(new JcheckBox())));
//添加一个spinner属性,其渲染编辑组件是 JSpinner
props.add(new BeanProperty("spinner"+i, new SpinnerCell(new JSpinner())));
}
//设置这些属性数组到属性表
table.setProperties(props);
add(new JScrollPane(table), BorderLayout.CENTER);
}
public static void main(String args[]) {
try {
//设置外观,这儿设置成系统的,也可以设置成其他的外观
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
ex.printStackTrace();
}
EventQueue.invokeLater(new Runnable() {
public void run() {
//显示属性表的窗口
PropertyDemo demo=new PropertyDemo();
demo.setSize(200,300);
demo.setVisible(true);
}
});
}
}
另外,在SpinnerCell类中继承并覆盖了checkComponentValue,检查整型值是否小于零,小于零就报错:
public class SpinnerCell extends TableCellSupport {
...
protected void checkComponentValue(JSpinner component) throws Exception {
Integer i=(Integer)component.getValue();
if(i.intValue()<0)
throw new Exception("Cannot be negative!");
}
}
当编辑spinner属性时,如果输入值小于零,便会提示用户出错,要求用户重新输入:
本例中的TableCellSupport封装了最常见的渲染和编辑逻辑,它把暗含的接口关系都已经实现了,用户的自定义扩展只需要实现上文提到的四个方法便可满足大部分需求,因此TableCellSupport类可以作为一个比较通用的基类,来简化自定义JTable的开发。
================================
到此为止,Swing框架中的渲染器部分就已经讲完了。至此,Swing框架就已经完全搭建了起来,但目前还缺少Swing与底层事件系统的交互,它通过AWT的事件循环系统来推动整个系统的运行。之后将补充一篇文章,讲述Swing系统的这个“第一推动力”。
文章引用自:
public class CheckBoxCell extends TableCellSupport{
public CheckBoxCell(JCheckBox cb){
super(cb);
cb.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
//被选中时需要触发编辑停止事件,一般直接调用父类的stopCellEditing即可,那儿已经负责了有效性检查,事件触发。
stopCellEditing();
}
});
}
protected void setValueTo(JCheckBox component, Object value) {
//认为value值是Boolean类型的,注意空值的处理
component.setSelected(value==null?false:((Boolean)value).booleanValue());
}
protected Object getValueFrom(JCheckBox component) {
//返回当前选中状态的布尔值,用Boolean封装
return new Boolean(component.isSelected());
}
}
BeanProperty是一个简单的封装类,目的是打包一个属性的描述,包括它的名称、值、渲染器和编辑器。它的代码如下:
public class BeanProperty {
//属性显示名称,属性表格第一列显示名
private String displayName;
//属性的值,属性表格第二列的值
private Object value;
//渲染和编辑属性值用的渲染编辑器(Support类)
private TableCellSupport support;
public BeanProperty(String displayName, TableCellSupport support) {
this.displayName=displayName;
this.support=support;
}
public String getDisplayName() {...}
public Object getValue() {...}
public void setValue(Object value) {...}
public TableCellSupport getSupport() {...}
}
BeanPropertyTable继承了JTable类,它覆盖了JTable的两方法getCellRenderer和getCellEditor来提供自定义的渲染器和编辑器,并使用基于BeanProperty数组的数据模型BeanModel:
public class BeanPropertyTable extends JTable {
...
private ArrayList properties;
public BeanPropertyTable() {
properties=new ArrayList();
...
}
public void setProperties(ArrayList properties){
if(properties!=null){
this.properties=properties;
setModel(new BeanModel());
}
}
//自定义的TableModel
private class BeanModel extends AbstractTableModel{
public int getRowCount() {
//属性表的行数
return properties.size();
}
public int getColumnCount() {
//属性表的列数
return 2;
}
public String getColumnName(int columnIndex) {
//属性表的列名:property, value
return columnIndex==0?"property":"value";
}
public boolean isCellEditable(int rowIndex, int columnIndex) {
//第二列属性值可编辑
return columnIndex==1;
}
public Object getValueAt(int rowIndex, int columnIndex) {
//获取值,第一列用属性显示名,第二列用属性值
BeanProperty property=properties.get(rowIndex);
return columnIndex==0?property.getDisplayName():property.getValue();
}
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
if(columnIndex==1)//只有第二列可编辑,设置第二列到属性值
properties.get(rowIndex).setValue(aValue);
}
}
//覆盖父类的getCellRenderer提供个性化的渲染器
public TableCellRenderer getCellRenderer(int row, int column) {
if(column==0)//第一列使用继承的渲染器
return super.getCellRenderer(row, column);
else//第二列使用属性对象自己提供的渲染器,注意Support类实现了TableCellRenderer
return properties.get(row).getSupport();
}
//覆盖父类的getCellEditor提供个性化的编辑器
public TableCellEditor getCellEditor(int row, int column) {
if(column==0)//第一列使用继承的编辑器
return super.getCellEditor(row,column);
else//第二列使用属性对象自己提供的编辑器,注意Support类实现了TableCellEditor
return properties.get(row).getSupport();
}
}
最后看看主类PropertyDemo.java的实现,非常简单,非常直接:
public class PropertyDemo extends JFrame{
public PropertyDemo() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
BeanPropertyTable table=new BeanPropertyTable();
ArrayList props=new ArrayList();
for(int i=0;i<2;i++){
//添加一个textfield属性,其渲染编辑组件是JTextField
props.add(new BeanProperty("textfield"+i,new TextFieldCell(new JTextField())));
//添加一个combo属性,其渲染编辑组件是JComboBox
JComboBox cb=new JComboBox();
cb.addItem("true");
cb.addItem("false");
props.add(new BeanProperty("combobox"+i,new ComboBoxCell(cb)));
//添加一个checkbox属性,其渲染编辑组件是 JCheckBox
props.add(new BeanProperty("checkbox"+i,new CheckBoxCell(new JcheckBox())));
//添加一个spinner属性,其渲染编辑组件是 JSpinner
props.add(new BeanProperty("spinner"+i, new SpinnerCell(new JSpinner())));
}
//设置这些属性数组到属性表
table.setProperties(props);
add(new JScrollPane(table), BorderLayout.CENTER);
}
public static void main(String args[]) {
try {
//设置外观,这儿设置成系统的,也可以设置成其他的外观
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
ex.printStackTrace();
}
EventQueue.invokeLater(new Runnable() {
public void run() {
//显示属性表的窗口
PropertyDemo demo=new PropertyDemo();
demo.setSize(200,300);
demo.setVisible(true);
}
});
}
}
另外,在SpinnerCell类中继承并覆盖了checkComponentValue,检查整型值是否小于零,小于零就报错:
public class SpinnerCell extends TableCellSupport {
...
protected void checkComponentValue(JSpinner component) throws Exception {
Integer i=(Integer)component.getValue();
if(i.intValue()<0)
throw new Exception("Cannot be negative!");
}
}
当编辑spinner属性时,如果输入值小于零,便会提示用户出错,要求用户重新输入:
本例中的TableCellSupport封装了最常见的渲染和编辑逻辑,它把暗含的接口关系都已经实现了,用户的自定义扩展只需要实现上文提到的四个方法便可满足大部分需求,因此TableCellSupport类可以作为一个比较通用的基类,来简化自定义JTable的开发。
================================
到此为止,Swing框架中的渲染器部分就已经讲完了。至此,Swing框架就已经完全搭建了起来,但目前还缺少Swing与底层事件系统的交互,它通过AWT的事件循环系统来推动整个系统的运行。之后将补充一篇文章,讲述Swing系统的这个“第一推动力”。
文章引用自: