[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行数(自动换行时的逻辑行数)的组件发出来)

你可能感兴趣的:(checkbox)