[Swing扩展组件分享]为JTable添加选择列(CheckBox)的包装类

    有个时候,我们需要在Table中添加一列来标识某行是否被选中(不同于自带的行选择),同时又不方便去修改原来TableModel。为了解决这种问题,我编写了下面这个包装类(不知这么称呼是否恰当哈)。

CheckBoxTableModelProxy
package trytocatch.swingplus.table;

import java.util.BitSet;

import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
/**
 * 将一个TableMode封装成新的AbstractTableModel,在其中添加一列,用以标识是否被选中
 * @author [email protected]
 * @date 2013-4-30
 */
public class CheckBoxTableModelProxy extends AbstractTableModel {
    private static final long serialVersionUID = 4419095637613592975L;
    protected TableModel tableModel;
    protected String checkBoxColumnName;
    protected BitSet checkBitSet;
    protected int checkBoxIndex;

    public CheckBoxTableModelProxy(TableModel model,
            String checkBoxColumnName) {
        this(model, checkBoxColumnName, 0);
    }

    public CheckBoxTableModelProxy(TableModel model,
            String checkBoxColumnName, int checkBoxIndex) {
        if (model == null)
            throw new IllegalArgumentException("model can't be null");
        if (checkBoxIndex > model.getColumnCount())
            throw new IllegalArgumentException(
                    "checkBoxIndex can't be greater than colunm size");
        this.tableModel = model;
        this.checkBoxColumnName = checkBoxColumnName;
        this.checkBoxIndex = checkBoxIndex < 0 ? 0 : checkBoxIndex;
        checkBitSet = new BitSet();
        model.addTableModelListener(new EventRedirector());
    }

    @Override
    public int getRowCount() {
        return tableModel.getRowCount();
    }

    @Override
    public int getColumnCount() {
        return tableModel.getColumnCount() + 1;
    }

    @Override
    public String getColumnName(int columnIndex) {
        if (columnIndex == checkBoxIndex)
            return checkBoxColumnName;
        else if (columnIndex > checkBoxIndex)
            return tableModel.getColumnName(columnIndex - 1);
        else
            return tableModel.getColumnName(columnIndex);
    }

    @Override
    public Class getColumnClass(int columnIndex) {
        if (columnIndex == checkBoxIndex)
            return Boolean.class;
        else if (columnIndex > checkBoxIndex)
            return tableModel.getColumnClass(columnIndex - 1);
        else
            return tableModel.getColumnClass(columnIndex);
    }

    @Override
    public boolean isCellEditable(int rowIndex, int columnIndex) {
        if (columnIndex == checkBoxIndex)
            return true;
        else if (columnIndex > checkBoxIndex)
            return tableModel.isCellEditable(rowIndex, columnIndex - 1);
        else
            return tableModel.isCellEditable(rowIndex, columnIndex);
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        if (columnIndex == checkBoxIndex)
            return checkBitSet.get(rowIndex);
        else if (columnIndex > checkBoxIndex)
            return tableModel.getValueAt(rowIndex, columnIndex - 1);
        else
            return tableModel.getValueAt(rowIndex, columnIndex);
    }

    @Override
    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
        if (columnIndex == checkBoxIndex){
            checkBitSet.set(rowIndex, (Boolean) aValue);
            fireTableChanged(new TableModelEvent(this, rowIndex, rowIndex, columnIndex));
        } else if (columnIndex > checkBoxIndex)
            tableModel.setValueAt(aValue, rowIndex, columnIndex - 1);
        else
            tableModel.setValueAt(aValue, rowIndex, columnIndex);

    }

    public int[] getCheckedIndexes() {
        int[] indexes = new int[checkBitSet.cardinality()];
        int nextIndex = -1;
        for (int n = 0; n < indexes.length; n++) {
            nextIndex = checkBitSet.nextSetBit(nextIndex + 1);
            indexes[n] = nextIndex;
        }
        return indexes;
    }

    private class EventRedirector implements TableModelListener {
        @Override
        public void tableChanged(TableModelEvent e) {
            int Column = e.getColumn();
            if (Column >= checkBoxIndex)
                Column++;
            fireTableChanged(new TableModelEvent(CheckBoxTableModelProxy.this,
                    e.getFirstRow(), e.getLastRow(), Column, e.getType()));
            if (e.getType() != TableModelEvent.UPDATE//insert,delete
                    || e.getLastRow() == Integer.MAX_VALUE//fireTableDataChanged
                    || e.getLastRow() == TableModelEvent.HEADER_ROW) {//fireTableStructureChanged
                checkBitSet.clear();
                fireTableChanged(new TableModelEvent(
                        CheckBoxTableModelProxy.this, 0, getRowCount() - 1,
                        checkBoxIndex, TableModelEvent.UPDATE));
            }
        }
    }
}

    使用很方便,new CheckBoxTableModelProxy(oldTableModel,checkBoxColumnName)就可以得到一个添加了选择列的AbstractTableModel,再将它添加到JTable中即可,当然,也可以指定这一列的添加位置(使用另一个构造方法)。

    需要注意的是,如果原来的TableModel有行被添加或删除,或整个TableModel发生了变化,那么该Model中的选择列将被清空,如果只是单元格或行的数据更新,不会发生此问题,具体细节可查看EventRedirector。

    一直对设计模式的分类不太感冒,不知道这里使用的是不是代理模式。

    若对源码有疑问,欢迎提问或讨论。

    玩Swing的人已经很少了,如果该文章对你有用,还请推荐或留言支持一下,也算是给予我继续下去的动力。(PS:如果感兴趣的人多,接下来我把那个显示JTextArea行数(自动换行时的逻辑行数)的组件发出来)

你可能感兴趣的:([Swing扩展组件分享]为JTable添加选择列(CheckBox)的包装类)