概要
JTable
のセルに行選択チェックボックスを設定してキー操作なしで複数行選択を可能にします。
Screenshot
Advertisement
サンプルコード
class CheckBoxTable extends JTable {
private static final int CHECKBOX_COLUMN = 2;
private transient MouseListener handler;
private transient ListSelectionListener listener;
private int checkedIndex = -1;
protected CheckBoxTable(TableModel model) {
super(model);
}
@Override public void updateUI() {
addMouseListener(handler);
getSelectionModel().removeListSelectionListener(listener);
super.updateUI();
setSelectionModel(new CheckBoxListSelectionModel());
handler = new CheckBoxListener();
addMouseListener(handler);
listener = e -> {
if (checkedIndex < 0) { // e.getValueIsAdjusting();
ListSelectionModel sm = (ListSelectionModel) e.getSource();
TableModel model = getModel();
for (int i = 0; i < getRowCount(); i++) {
model.setValueAt(sm.isSelectedIndex(i), i, CHECKBOX_COLUMN);
}
}
};
getSelectionModel().addListSelectionListener(listener);
}
@Override public Component prepareEditor(
TableCellEditor editor, int row, int column) {
Component c = super.prepareEditor(editor, row, column);
if (c instanceof JCheckBox) {
JCheckBox b = (JCheckBox) c;
boolean selected = getSelectionModel().isSelectedIndex(row);
b.setBackground(
selected ? getBackground() : getSelectionBackground());
checkedIndex = row;
} else {
checkedIndex = -1;
}
return c;
}
private final class CheckBoxListSelectionModel
extends DefaultListSelectionModel {
@Override public void setSelectionInterval(int anchor, int lead) {
if (checkedIndex < 0) {
super.setSelectionInterval(anchor, lead);
} else {
EventQueue.invokeLater(() -> {
if (checkedIndex >= 0
&& lead == anchor
&& checkedIndex == anchor) {
super.addSelectionInterval(checkedIndex, checkedIndex);
} else {
super.setSelectionInterval(anchor, lead);
}
});
}
}
@Override public void removeSelectionInterval(
int index0, int index1) {
if (checkedIndex < 0) {
super.removeSelectionInterval(index0, index1);
} else {
EventQueue.invokeLater(() ->
super.removeSelectionInterval(index0, index1));
}
}
}
private final class CheckBoxListener extends MouseAdapter {
@Override public void mousePressed(MouseEvent e) {
JTable table = (JTable) e.getComponent();
Point pt = e.getPoint();
if (table.columnAtPoint(pt) == CHECKBOX_COLUMN) {
int row = table.rowAtPoint(pt);
checkedIndex = row;
ListSelectionModel sm = table.getSelectionModel();
if (sm.isSelectedIndex(row)) {
sm.removeSelectionInterval(row, row);
} else {
sm.addSelectionInterval(row, row);
}
} else {
checkedIndex = -1;
}
table.repaint();
}
}
}
View in GitHub: Java, Kotlin解説
JTable#prepareEditor(...)
をオーバーライドしてJCheckBox
をセルレンダラー、セルエディタとして使用する列がクリックされたかを判断- 対象列がクリックされた場合はその行、そうでない場合は
-1
をcheckedIndex
に記憶
- 対象列がクリックされた場合はその行、そうでない場合は
ListSelectionListener
JTable#getSelectionModel()
で取得した行選択モデルListSelectionModel
にListSelectionListener
を追加し、JCheckBox
の選択状態と行選択状態を同期させるcheckedIndex < 0
で以外のセルが選択された場合、全行にTableModel#setValueAt(ListSelectionModel#isSelectedIndex(i), i, CHECKBOX_COLUMN)
を実行することで同期
DefaultListSelectionModel
DefaultListSelectionModel#setSelectionInterval(...)
、DefaultListSelectionModel#removeSelectionInterval(...)
をオーバーライドしてJCheckBox
が設定された対象列が選択された場合(checkedIndex >= 0
)の動作を変更- これらの選択状態の設定、削除を
EventQueue.invokeLater(...)
であとで実行することで通常の選択動作を上書き DefaultListSelectionModel#setSelectionInterval(...)
での選択状態設定をCtrl+クリックと同じになるようDefaultListSelectionModel#addSelectionInterval(...)
に変更して他の行の選択状態を維持したままクリックした行を選択状態に追加
MouseListener
JTable
にMouseListener
を追加し、JCheckBox
の選択状態と行選択状態を同期させるMouseListener#mousePressed(...)
をオーバーライドして行が選択状態ならDefaultListSelectionModel#removeSelectionInterval(...)
、未選択状態ならDefaultListSelectionModel#addSelectionInterval(...)
を実行