概要

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は不定状態になる

参考リンク

コメント