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,该项为可选做项
   现在它们之间不在暗含什么关系,需要实现和覆盖的方法的含义也非常清晰

你可能感兴趣的:(编程,框架,工作,swing,ide)