群里看到有的同学需要在某 JTable 内容的基础上,多出一列 checkbox,用于某种多选,不是第一次看到这种需求了,所以写了这个“通用”的代理实现。
大体写了一下,没有仔细处理原TableModel的事件,所以使用的时候要注意原TableModel不能fire基于cell的更新事件,不能添加删除列。
先扔这里,有时间再琢磨。
/*
* Copyright 2014 raistlic@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* Currently not supporting column insertion or deletion, that is, if the wrapped (original) table
* model inserted or deleted a column, not sure if the table will correctly pick up the event and
* works fine afterwards, behaviour unknown.
*
* <p/>
* Also not supporting cell based update event which column index is greater than the selectable
* column.
*
* TODO: Builder and its methods not Java doc-ed yet.
* TODO: properly delegate & translate events for the original table model.
*
* @author raistlic
* @since 2014-08-26
*/
public class RowSelectableTableModel extends AbstractTableModel implements TableModel {
public static RowSelectableTableModel wrap(TableModel tableModel) {
return builderFor(tableModel).build();
}
public static Builder builderFor(TableModel tableModel) {
if (tableModel == null) {
throw new NullPointerException("'tableModel' is null.");
}
return new Builder(tableModel);
}
public static class Builder {
private TableModel tableModel;
private int limit;
private int selectableColumnIndex;
private String selectableColumnName;
private Builder(TableModel tableModel) {
this.tableModel = tableModel;
this.limit = 0;
selectableColumnIndex = 0;
selectableColumnName = "Select";
}
public Builder withSelectionLimit(int limit) {
this.limit = limit;
return this;
}
public Builder withSelectableColumnAt(int column) {
if (column < 0) {
throw new IndexOutOfBoundsException("Selectable column index out of bounds: " + column);
}
if (column > tableModel.getColumnCount()) {
throw new IndexOutOfBoundsException(
"Selectable column index out of bounds: " + column + " / " + tableModel.getColumnCount());
}
this.selectableColumnIndex = column;
return this;
}
public Builder withSelectableColumnName(String name) {
this.selectableColumnName = (name == null) ? "" : name;
return this;
}
public RowSelectableTableModel build() {
RowSelectableTableModel result = new RowSelectableTableModel(this);
tableModel.addTableModelListener(result.new OriginalModelAdapter());
return result;
}
}
private final TableModel tableModel;
private final int limit;
private final int selectableColumnIndex;
private final String selectableColumnName;
private Set<Integer> selected;
private int selectionBasedRowCount;
private RowSelectableTableModel(Builder builder) {
this.tableModel = builder.tableModel;
this.limit = builder.limit;
this.selectableColumnIndex = builder.selectableColumnIndex;
this.selectableColumnName = builder.selectableColumnName;
this.selectionBasedRowCount = tableModel.getRowCount();
this.selected = new HashSet<Integer>();
}
public Set<Integer> getSelectedRows() {
// defensive copy:
return new HashSet<Integer>(selected);
}
@Override
public int getRowCount() {
return tableModel.getRowCount();
}
@Override
public int getColumnCount() {
return 1 + tableModel.getColumnCount();
}
@Override
public String getColumnName(int columnIndex) {
if (columnIndex < selectableColumnIndex) {
return tableModel.getColumnName(columnIndex);
}
else if (columnIndex == selectableColumnIndex) {
return selectableColumnName;
}
else {
return tableModel.getColumnName(columnIndex - 1);
}
}
@Override
public Class<?> getColumnClass(int columnIndex) {
if (columnIndex < selectableColumnIndex) {
return tableModel.getColumnClass(columnIndex);
}
else if (columnIndex == selectableColumnIndex) {
return boolean.class;
}
else {
return tableModel.getColumnClass(columnIndex - 1);
}
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
if (columnIndex < selectableColumnIndex) {
return tableModel.isCellEditable(rowIndex, columnIndex);
}
else if (columnIndex == selectableColumnIndex) {
return limit <= 0 || selected.size() < limit;
}
else {
return tableModel.isCellEditable(rowIndex, columnIndex - 1);
}
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
if (columnIndex < selectableColumnIndex) {
return tableModel.getValueAt(rowIndex, columnIndex);
}
else if (columnIndex == selectableColumnIndex) {
return selected.contains(rowIndex);
}
else {
return tableModel.getValueAt(rowIndex, columnIndex - 1);
}
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
if (columnIndex < selectableColumnIndex) {
tableModel.setValueAt(aValue, rowIndex, columnIndex);
}
else if (columnIndex == selectableColumnIndex) {
@SuppressWarnings("unchecked")
boolean value = (Boolean) aValue;
if (value) {
selected.add(rowIndex);
}
else {
selected.remove(rowIndex);
}
super.fireTableCellUpdated(rowIndex, columnIndex);
}
else {
tableModel.setValueAt(aValue, rowIndex, columnIndex - 1);
}
}
@Override
public void addTableModelListener(TableModelListener listener) {
super.addTableModelListener(listener);
tableModel.addTableModelListener(listener);
}
@Override
public void removeTableModelListener(TableModelListener listener) {
super.removeTableModelListener(listener);
tableModel.removeTableModelListener(listener);
}
private class OriginalModelAdapter implements TableModelListener {
@Override
public void tableChanged(TableModelEvent e) {
if (e.getSource() != tableModel) {
return;
}
if (e.getType() != TableModelEvent.DELETE) {
return;
}
int newRowCount = tableModel.getRowCount();
boolean selectionUpdated = false;
if (newRowCount < selectionBasedRowCount) {
for (Iterator<Integer> iterator = selected.iterator(); iterator.hasNext(); ) {
if (iterator.next() >= newRowCount) {
iterator.remove();
selectionUpdated = true;
}
}
}
selectionBasedRowCount = newRowCount;
if (selectionUpdated) {
RowSelectableTableModel.super.fireTableDataChanged();
}
}
}
}
RowSelectableTableModel proxy for TableModel
原文地址:http://blog.csdn.net/raistlic/article/details/38843887