Swing框架之Renderer之四

昨天文章介绍JTable的TableCellEditor时提到,TableCellEditor接口中的方法暗含许多关系,如过实现不能正确建立这些关系,会导致自定义的编辑器不能正常工作。记住这些关系往往使人很苦恼。有没有办法来封装这些关系,只留下简单的编程接口?办法就是写一个TableCellEditor的子类,所谓的框架支持(Support)类,将暗含关系进行封装,将需要自定义扩展的特征留给Support子类来完成。
本文举一例子,实现如下图所示、类似于IDE界面设计工具的属性表。此例将向你演示如何自定义扩展TableCellEditor、TableCellRenderer以及TableModel,以便将这几天讲的知识串一下。

此例中属性表区别于普通表的特征包括:
1.属性值一列不同行显示不同类型的数据。
2.属性值单元格采用不同的渲染组件和编辑组件。
3.属性值单元格的编辑器和渲染组件是同一种组件。
该例子包括如下图所示八个文件:

TableCellSupport实现了TableCellEditor和TableCellRenderer两个接口,是扩展自定义渲染器及编辑器的基类。该类实现了TableCellEditor和TableCellRenderer的所有方法,封装了TableCellEditor所暗含的关系,并假定负责渲染的组件和负责编辑的组件为同一组件。org.dyno.test.impl包下的类继承该TableCellSupport类,分别实现了CheckBox、 ComboBox、 Spinner以及TextField常见类型的渲染器编辑器。要实现其他类型的自定义渲染器编辑器,可继承TableCellSupport进行扩展。
BeanProperty只是简单封装了某JavaBean属性以及渲染编辑器的对象。
BeanPropertyTable继承JTable,以BeanProperty数组作为数据源。
PropertyDemo是该例子的主类,是一个JFrame。
TableCellSupport是实现该属性表编辑器的核心类,下面是TableCellSupport的实现:
public abstract class TableCellSupport
implements TableCellEditor, TableCellRenderer {
//编辑器、渲染器缺省的前后背景
static Color BACKGROUND=UIManager.getColor("TextField.background");
static Color FOREGROUND=UIManager.getColor("TextField.foreground");
static Color SELECTION_BACKGROUND=UIManager.getColor("TextField.selectionBackground");
static Color SELECTION_FOREGROUND=UIManager.getColor("TextField.selectionForeground");
//渲染器、编辑器的组件,使用同一个
protected T component;
//CellEditorListener的容器,使用WeakReference放置内存泄漏
private ArrayList> listeners
=new ArrayList>();
//构造函数
public TableCellSupport(T component) {
this.component=component;
//如果是JComponent类组件,为了美观把边框去掉
if(component instanceof JComponent)
((JComponent)component).setBorder(null);
}
//获取并配置编辑组件
public Component getTableCellEditorComponent(JTable table,
Object value, boolean isSelected, int row, int column) {
//将value值设置给component,这儿调用了一个子类需要实现的方法setValueTo
setValueTo(component, value);
//设置前后景、字体
component.setBackground(BACKGROUND);
component.setForeground(FOREGROUND);
component.setFont(table.getFont());
return component;
}
//获取当前编辑器的值,以component中的值为准
public Object getCellEditorValue() {
//调用了一个子类需要实现的方法getValueFrom从component获取当前正在编辑的值
return getValueFrom(component);
}
//根据事件anEvent判断是否可编辑,直接返回true,如有特殊需求子类可以覆盖改变
public boolean isCellEditable(EventObject anEvent) {
return true;
}
//根据事件anEvent判断是否可选,直接返回true,如有特殊需求子类可以覆盖改变
public boolean shouldSelectCell(EventObject anEvent) {
return true;
}
//停止编辑
public boolean stopCellEditing() {
try{
//调用通常子类需要覆盖的方法:checkComponentValue,该方法通过抛出异常来声明发生何中错误
checkComponentValue(component);
//通过检查,说明有效,触发事件通知编辑停止事件
fireEditingStopped();
//返回true标识成功
return true;
}catch(Exception e){
//说明有错,错误信息被包含在Exception的message中,显示该信息。
JOptionPane.showMessageDialog(component,
e.getMessage(), "Error Input", JoptionPane.ERROR_MESSAGE);
//返回false标识失败
return false;
}
}
//取消编辑
public void cancelCellEditing() {
//通常直接发出通知即可
fireEditingCanceled();
}
//添加CellEditorListener
public void addCellEditorListener(CellEditorListener l) {
listeners.add(new WeakReference(l));
}
//删除CellEditorListener
public void removeCellEditorListener(CellEditorListener l) {
listeners.remove(new WeakReference(l));
}
//获取并配置渲染组件
public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column) {
//设置组件的值
setValueTo(component, value);
//设置字体、前后背景
component.setFont(table.getFont());
if(isSelected){
component.setBackground(SELECTION_BACKGROUND);
component.setForeground(SELECTION_FOREGROUND);
}else{
component.setBackground(BACKGROUND);
component.setForeground(FOREGROUND);
}
//返回该组件
return component;
}
//触发编辑停止操作事件,注意这儿是protected方法,允许子类调用
protected void fireEditingStopped(){
ChangeEvent e=new ChangeEvent(component);
for(WeakReference ref:listeners){
CellEditorListener l=ref.get();
l.editingStopped(e);
}
}
//触发编辑取消操作,允许子类调用
protected void fireEditingCanceled(){
ChangeEvent e=new ChangeEvent(component);
for(WeakReference ref:listeners){
CellEditorListener l=ref.get();
l.editingCanceled(e);
}
}
//检查编辑器组件component内的值是否有效,对于希望检查有效性的需要覆盖此方法
protected void checkComponentValue(T component)throws Exception{
}
//将value设置到编辑组件component内,子类必须实现的抽像方法
protected abstract void setValueTo(T component, Object value);
//从编辑组件component内获取正在编辑的值,子类必须实现的抽象方法
protected abstract Object getValueFrom(T component);
}
通过封装,TableCellSupport实现了大部分自定义渲染器和编辑器的功能,继承TableCellSupport的类要实现以下方法:
1.带有以组件为参数的构造函数,还负责可能的事件注册,注册可能导致编辑完成事件的处理器。
2.setValueTo:将给定的值设置到给定的组件内
3.getValueFrom:从指定组件内获取当前编辑的值
3.如需要判断编辑器的值是否有效,还要覆盖checkComponentValue,该项为可选做项
现在它们之间不在暗含什么关系,需要实现和覆盖的方法的含义也非常清晰
阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页