作者长期从事网管软件的开发工作网络设备的配置管理模块需要对网络设备的大量参数进行配置工作设计开发配置管理模块的界面成为整个网管系统的难点尤其是用Java语言开发时需要编制大量的界面布局事件处理等代码网管软件开发的主要工作量就集中在此制作一个类似于DelphiJBuilder等可视化开发工具的对象查看器的参数配置控件用于项目的优点是显而易见的
界面显得很专业
容易做到使整个系统的风格趋于一致
使用灵活代码量大大减小
……
最终的使用效果如下图所示
作者本人把它称为属性编辑器它的主要特征是是一个两列多行的表格第一列用来显示属性名称第二列用来显示和编辑属性值属性值的显示和编辑可以有多种方式如文本框输入下拉框选择组合框选择自定义的弹出式对话框等当属性被编辑后可以向感兴趣的对象发出通知
下面就属性编辑器的设计思路和编制过程进行具体的解释说明
设计思路
根据属性编辑器的主要界面特征选择JTable作为编辑器的基类JTable是Swing中最复杂的组件之一它主要被用来显示数据行和数据列它可以为每个数据单元分别提供绘制器和编辑器是典型的MVC(模型Model视图View控制Control)模式的实现者
设计属性编辑器为一个两列的JTable每一行数据的第一列存放不重复的字符串作为属性的名称第二列保存Object对象根据其具体数据类型设置其单元绘制器和单元编辑器一般单元绘制器用系统默认的JLabel即可而单元编辑器则必须提供定制的控件如对字符串型数据用JTextField或者JComboBox对布尔型数据用JCheckBox对特殊类型可以提供JDialog对话框的主要界面可以由使用本属性编辑器的程序员自行定制
属性编辑器还要提供相关接口如属性值是否只读属性值变化时必须通知监听者等等
属性编辑器的具体设计请参见下图限于篇幅该图只简单列举了几个主要的类之间的关系和一些重要的变量和方法
属性编辑器的制作过程
属性编辑器是从JTable上继承下来的必须给它定义一个DefaultTableModel的子类用来作为属性编辑器的数据模型存放属性名和属性值因为它只在属性编辑器内部使用所以可以定义为属性编辑器的内部类
public class PropertyEditor extends JTable {
protected class PropertyEditorModel extends DefaultTableModel{
public PropertyEditorModel() {
super( ); // 只有两个列
}
public String getColumnName(int col) {
return ; // 不需要列标题
}
public boolean isCellEditable(int row int col) {
if(col == )
return false; // 第一列是属性名不可编辑
else
// 属性值是否可编辑要看用户指定的情况
return ((Boolean)propertyEditableget(thisgetValueAt(row )))booleanValue();
}
}
}
要实现定制的单元绘制器和编辑器必须覆盖JTable的getCellEditor和getCellRenderer方法那些已经做好的绘制器编辑器和该属性值是否允许编辑都可以根据属性名保存在Hashtable里需要的时候根据属性名取出来
/**
* 每一个属性项都对应一个单元编辑器用Hashtable来保存这些编辑器
*/
protected Hashtable propertyEditors = new Hashtable();
/**
* 每一个属性项都对应一个单元渲染器
*/
protected Hashtable propertyRenderers = new Hashtable();
/**
* 属性是否可编辑
*/
protected Hashtable propertyEditable = new Hashtable();
/**
* 获取指定单元格的编辑器
* @param row 行
* @param col 列
*/
public TableCellEditor getCellEditor(int row int col) {
TableCellEditor editor = null;
if(col == ) { // 属性值列才需要编辑器这个判断条件不要也可效率会低一点
editor = (TableCellEditor)propertyEditorsget(thisgetValueAt(row ));
}
if(editor == null) { // 没找到编辑器则用系统默认的
editor = supergetCellEditor(row col);
}
return editor;
}
/**
* 获取指定单元格的渲染器
*/
public TableCellRenderer getCellRenderer(int row int col) {
TableCellRenderer renderer = null;
if(col == ) {
renderer = (TableCellRenderer)propertyRenderersget(thisgetValueAt(row ));
}
if(renderer == null) {
renderer = supergetCellRenderer(row col);
}
// 给表格元素提供Hint提示
if(renderer instanceof JComponent) {
Object v = thisgetModel()getValueAt(row col);
if(v == null) { // 属性值有可能为空则取属性名属性名必不为空
v = thisgetModel()getValueAt(row );
}
((JComponent)renderer)setToolTipText(vtoString());
}
return renderer;
}
如何确定哪个属性用哪一种编辑器呢?可以根据用户程序员传入的参数来确定对传入的整数型数据则用LongCellEditor字符串型的当然用StringCellEditor了其它依次类推以整数型来举例
/**
* 在属性表中增加整数属性允许为空值编辑器和渲染器为long型编辑器和渲染器
* 当属性值为空值时必须写成
* addProperty(pName (Long)null)
* @param propertyName 属性名
* @param longNumObj 属性初始值
*/
public void addProperty(String propertyName Long longNumObj) {
if(propertyName == null) throw new RuntimeException(Coding error : property name can NOT be null !);
Object[] row = new Object[];
row[] = propertyName;
row[] = longNumObj;
appendRow(row); // 往表格增加行
propertyEditorsput(propertyName longEditor); // 添加整型编辑器
propertyRenderersput(propertyName longRenderer); // 添加整型绘制器
propertyEditableput(propertyName new Boolean(true)); // 设置该属性允许编辑
}
给属性编辑器加上get和set接口
/**
* 根据属性名得到属性值
* @param propertyName 属性名
*/
public Object getPropertyValue(String propertyName) {
Object retValue = null;
for(int i = ; i < ptm.getRowCount(); i++) {
if(ptm.getValueAt(i, 0).equals(propertyName)) {
retValue = ptm.getValueAt(i, 1);
break;
}
}
return retValue;
}
/**
* 设置属性值
* @param propertyName 属性名
* @param newValue 新的属性值
*/
public void setPropertyValue(String propertyName, Object newValue) {
for(int i = 0; i < ptm.getRowCount(); i++) {
if(ptm.getValueAt(i, 0).equals(propertyName)) {
ptm.setValueAt(newValue, i, 1);
break;
}
}
}
好了,属性值编辑器的大框架已经完成了,下面以整数型的单元编辑器为例,简单说明单元编辑器的制作方法,双精度型和字符串型的和它类似,最复杂的用户自定义对话框型的,留待读者自己看源代码吧(反正源代码里面有详细的注释的J)。Tw.WinGwiT.Com
/**
* 创建并初始化long型数据的编辑器和渲染器
*/
private void createLongEditorRenderer() {
final JTextField longTextField = new JTextField("0", 5); // 用文本输入框做输入控件
longTextField.setHorizontalAlignment(JTextField.LEFT);
longEditor = new DefaultCellEditor(longTextField) {
private Object previousValue = null;
public Object getCellEditorValue() {
if(longTextField.getText().equals("")