有个时候,我们需要在Table中添加一列来标识某行是否被选中(不同于自带的行选择),同时又不方便去修改原来TableModel。为了解决这种问题,我编写了下面这个包装类(不知这么称呼是否恰当哈)。
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行数(自动换行时的逻辑行数)的组件发出来)