java swing的TableColumn属性设置后失效的原因与解决办法

前述

在java的swing开发中,JTable必然是一个常用的UI组件,而在JTable中常用的操作就是动态增删数据列。
不知道在使用JTable动态增删列的时候有没有发现,对TableColumn设置属性后再增加列会导致TableColumn的属性失效,下面就来对这个问题一探究竟。

TableColumn属性

TableColumn是JTable的列对象,通过对其设置属性(例如宽度,宽度拉伸操作等)可以控制整列的UI操作。
TableColumn常用的属性设置方法如下

方法作用
setHeaderValue设置表头
setHeaderRenderer设置自定义表头渲染器
setCellRenderer设置自定义单元格渲染器
setCellEditor设置自定义单元格编辑器
setWidth设置宽度
setPreferredWidth设置首选宽度(优先使用)
setMinWidth设置最小宽度(拉伸时使用)
setMaxWidth设置最大宽度(拉伸时使用)
setResizable设置是否允许拉伸

失效场景

// 创建表对象
JTable table=new JTable();
BaseTableModel tableModel = getModel();
// 增加表列
tableModel.addColumn("列1")

// 获取列对象
TableColumn tableColumn=table.getColumnModel().getColumn(0);
// 设置列对象属性
// 设置列单元格渲染器,将此列的渲染组件全部修改为复选框
tableColumn.setCellRenderer(new TableCellRenderer(){
	@Override
	public Component getTableCellRendererComponent(JTable table, Object value,
                                            boolean isSelected, boolean hasFocus,
                                            int row, int column){
         return new JCheckBox();                                   
    }
});

// 再次添加表列
tableModel.addColumn("列2")
tableModel.addColumn("列3")

执行上述代码,会发现列1的渲染器未生效,如果将列2列3的添加语句注释掉,则渲染器失效,可以推断出是后续添加的列导致了之前的列属性失效,但这是为什么了,让我们来一探究竟。

原因分析

tableModel.addColumn进行跟踪分析,可以看到如下情况:

这是DefaultTableModeladdColumn方法实现

public void addColumn(Object columnName, Vector columnData) {
        columnIdentifiers.addElement(columnName);
        if (columnData != null) {
            int columnSize = columnData.size();
            if (columnSize > getRowCount()) {
                dataVector.setSize(columnSize);
            }
            justifyRows(0, getRowCount());
            int newColumn = getColumnCount() - 1;
            for(int i = 0; i < columnSize; i++) {
                  Vector row = (Vector)dataVector.elementAt(i);
                  row.setElementAt(columnData.elementAt(i), newColumn);
            }
        }
        else {
            justifyRows(0, getRowCount());
        }

        fireTableStructureChanged();
    }

上述代码中,前面都是想tableModel中增加列的扩容操作,可以看到最后有一句代码fireTableStructureChanged(),跟踪下去可以看到下列代码:

 public void fireTableChanged(TableModelEvent e) {
     // Guaranteed to return a non-null array
     Object[] listeners = listenerList.getListenerList();
     // Process the listeners last to first, notifying
     // those that are interested in this event
     for (int i = listeners.length-2; i>=0; i-=2) {
         if (listeners[i]==TableModelListener.class) {
             ((TableModelListener)listeners[i+1]).tableChanged(e);
         }
     }
 }

上述代码仅是TableModel对表结构修改后的一个事件通知,并无具体操作,那么我们继续深入,跟踪到事件通知的监听器里看看。

 public void tableChanged(TableModelEvent e) {
        if (e == null || e.getFirstRow() == TableModelEvent.HEADER_ROW) {
            // The whole thing changed
            clearSelectionAndLeadAnchor();

            rowModel = null;

            if (sortManager != null) {
                try {
                    ignoreSortChange = true;
                    sortManager.sorter.modelStructureChanged();
                } finally {
                    ignoreSortChange = false;
                }
                sortManager.allChanged();
            }

            if (getAutoCreateColumnsFromModel()) {
                // This will effect invalidation of the JTable and JTableHeader.
                createDefaultColumnsFromModel();
                return;
            }

            resizeAndRepaint();
            return;
        }
        // 剩余代码是针对排序修改和行修改的操作,此处忽视...
    }

上面代码是跟踪到JTable的监听器方法,TableModel通过事件通知触发此方法,在这个方法中,可以看到有一句代码createDefaultColumnsFromModel(),根据字面意思是从表模型中创建默认的表列?究竟如何,还得进入细细探究。

public void createDefaultColumnsFromModel() {
    TableModel m = getModel();
    if (m != null) {
        // Remove any current columns
        TableColumnModel cm = getColumnModel();
        while (cm.getColumnCount() > 0) {
            cm.removeColumn(cm.getColumn(0));
        }

        // Create new columns from the data model info
        for (int i = 0; i < m.getColumnCount(); i++) {
            TableColumn newColumn = new TableColumn(i);
            addColumn(newColumn);
        }
    }
}
public void addColumn(TableColumn aColumn) {
    if (aColumn.getHeaderValue() == null) {
        int modelColumn = aColumn.getModelIndex();
        String columnName = getModel().getColumnName(modelColumn);
        aColumn.setHeaderValue(columnName);
    }
    getColumnModel().addColumn(aColumn);
}

终于真相大白了,原来每一次动态增加表列,TableColumnModel都会清空之前的列结构,然后从表模型中重新读取添加新的列,而每次的添加都只会从表模型中读取列头写入,所以就导致了后续添加表列后会导致之前的表列属性失效的问题。

解决方案

如果确实有上述动态增删表列的场景,可以在Table对象外缓存一份TableColumn的属性副本,每次增删表列后,重新对表列设置属性,则可以解决该问题。

不得不说,这个问题的出现是真的坑,没想到JTable内部对表列的增删是这么实现的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

暴走的怪兽君

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值