• category: swing folder: TableModelEvent title: JTableのモデルが変更されたことをイベントで受け取る tags: [JTable, TableModelListener, TableModelEvent, JCheckBox] author: aterai pubdate: 2014-03-31T00:21:38+09:00 description: JTableのモデルの変更を受け取ってJTableHeaderに追加したJCheckBoxを更新します。 image: https://lh3.googleusercontent.com/-Mndxsu0wtCM/Uzg00YuVfyI/AAAAAAAACCw/HoRS9CVP_-o/s800/TableModelEvent.png

概要

JTableのモデルの変更を受け取ってJTableHeaderに追加したJCheckBoxを更新します。

サンプルコード

class HeaderCheckBoxHandler implements TableModelListener {
  private final JTable table;
  private final int targetColumnIndex;
  public HeaderCheckBoxHandler(JTable table, int index) {
    this.table = table;
    this.targetColumnIndex = index;
  }
  @Override public void tableChanged(TableModelEvent e) {
    int vci = table.convertColumnIndexToView(targetColumnIndex);
    TableColumn column = table.getColumnModel().getColumn(vci);
    Object status = column.getHeaderValue();
    TableModel m = table.getModel();
    if (e.getType() == TableModelEvent.DELETE) {
      if (m.getRowCount() == 0) {
        column.setHeaderValue(Status.DESELECTED);
      } else if (Status.INDETERMINATE.equals(status)) {
        boolean selected = true;
        boolean deselected = true;
        for (int i = 0; i < m.getRowCount(); i++) {
          Boolean b = (Boolean) m.getValueAt(i, targetColumnIndex);
          selected &= b;
          deselected &= !b;
        }
        if (deselected) {
          column.setHeaderValue(Status.DESELECTED);
        } else if (selected) {
          column.setHeaderValue(Status.SELECTED);
        } else {
          return;
        }
      }
    } else if (e.getType() == TableModelEvent.INSERT
               && !Status.INDETERMINATE.equals(status)) {
      boolean selected = Status.DESELECTED.equals(status);
      boolean deselected = Status.SELECTED.equals(status);
      for (int i = e.getFirstRow(); i <= e.getLastRow(); i++) {
        Boolean b = (Boolean) m.getValueAt(i, targetColumnIndex);
        selected &= b;
        deselected &= !b;
      }
      if (selected && m.getRowCount() == 1) {
        column.setHeaderValue(Status.SELECTED);
      } else if (selected || deselected) {
        column.setHeaderValue(Status.INDETERMINATE);
      }
    } else if (e.getType() == TableModelEvent.UPDATE
               && e.getColumn() == targetColumnIndex) {
      if (Status.INDETERMINATE.equals(status)) {
        boolean selected = true;
        boolean deselected = true;
        for (int i = 0; i < m.getRowCount(); i++) {
          Boolean b = (Boolean) m.getValueAt(i, targetColumnIndex);
          selected &= b;
          deselected &= !b;
          if (selected == deselected) {
            return;
          }
        }
        if (deselected) {
          column.setHeaderValue(Status.DESELECTED);
        } else if (selected) {
          column.setHeaderValue(Status.SELECTED);
        } else {
          return;
        }
      } else {
        column.setHeaderValue(Status.INDETERMINATE);
      }
    }
    JTableHeader h = table.getTableHeader();
    h.repaint(h.getHeaderRect(vci));
  }
}
View in GitHub: Java, Kotlin

解説

上記のサンプルでは、0行目の値(boolean)の変更、行の追加、削除を受け取ってJTableHeaderのチェック状態を更新するTableModelListenerを作成して、これをTableModel#addTableModelListener(...)で設定しています。

  • 行の削除: e.getType() == TableModelEvent.DELETE
    • 削除によって行数が0になった場合は、JTableHeaderは未選択状態
    • JTableHeaderが選択状態、または未選択状態の場合、削除によってその状態は変化しない
    • JTableHeaderが不定状態の場合は、全行を検索して選択状態を変更するかどうかを調査する
  • 行の追加: e.getType() == TableModelEvent.INSERT
    • JTableHeaderがすでに不定状態の場合は、追加される行の選択状態にかかわらず、その状態は変化しない
    • JTableHeaderが不定状態でない場合は、追加される行とJTableHeaderの選択状態を合わせて調査する
  • 行(0列目)の更新: e.getType() == TableModelEvent.UPDATE
    • JTableHeaderが不定状態の場合は、全行を検索して選択状態を変更するかどうかを調査する
    • JTableHeaderが不定状態でない場合は、この更新によってJTableHeaderは不定状態になる

参考リンク

コメント