概要

セルの編集中、フォーカスが別のコンポーネントに移動した場合、その編集を確定する方法をテストします。

サンプルコード

table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
view all

解説

デフォルトのJTableでは、Tabキーやマウスのクリックなどで同じテーブルの別セルにフォーカスが移動すると編集が確定しますが、別のコンポーネントにフォーカスが移動しても編集は確定しません。

  • table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);を設定:
    • 同じフレームを親に持つコンポーネントにフォーカスが移動したとき、編集が確定する
    • 別フレームのコンポーネントにフォーカスが移動しても編集中のまま
    • ヘッダをクリック、入れ替え、サイズ変更すると、編集はキャンセルされる
  • DefaultCellEditorからエディタコンポーネントを取得し、FocusListenerを設定し、セルが編集中ならtable.getCellEditor().stopCellEditing();を実行:
    • 別フレームのコンポーネントにフォーカスが移動したときも、編集が確定する
    • ヘッダをクリック、入れ替え、サイズ変更すると、編集はキャンセルされる
DefaultCellEditor dce = (DefaultCellEditor) table.getDefaultEditor(Object.class);
dce.getComponent().addFocusListener(new FocusAdapter() {
  @Override public void focusLost(FocusEvent e) {
    if (!focusCheck.isSelected()) {
      return;
    }
    if (table.isEditing()) {
      table.getCellEditor().stopCellEditing();
    }
  }
});
  • TableHeaderMouseListenerを設定して、セルが編集中ならtable.getCellEditor().stopCellEditing(); を実行:
table.getTableHeader().addMouseListener(new MouseAdapter() {
  @Override public void mousePressed(MouseEvent e) {
    if (!headerCheck.isSelected()) {
      return;
    }
    if (table.isEditing()) {
      table.getCellEditor().stopCellEditing();
    }
  }
});

親フレームの状態変化でテーブルのヘッダのサイズ変更が発生する場合、ヘッダのリサイズモデルによって、編集中のセルの状態変化が異なります(JDK 1.7.0では修正済み: Bug ID: 4330950 Lost newly entered data in the cell when resizing column width)。

  • JTable.AUTO_RESIZE_OFF
    • 親フレームのリサイズや最大化は編集中のまま
  • JTable.AUTO_RESIZE_ALL_COLUMNSなど
    • 親フレームのリサイズや最大化を行うとヘッダのサイズが変化するため、キャンセル扱い

ヘッダのサイズが変化しない場合は、どちらの設定でも以下のようになります。

  • 親フレームの最小化(アイコン化)は編集中のまま
  • 親フレームを閉じる場合はキャンセル扱い

親フレームの状態変化に応じて編集の確定を行う場合は、以下のように、JTable#columnMarginChangedメソッドなどをオーバーライドしたり、各種リスナーを設定する必要があります。

table = new JTable(sorter) {
  @Override public void columnMarginChanged(ChangeEvent e) {
    if (table.isEditing()) {
      table.getCellEditor().stopCellEditing();
    }
    super.columnMarginChanged(e);
  }
};

frame.addWindowListener(new WindowAdapter() {
  @Override public void windowClosing(WindowEvent e) {
    if (table.isEditing()) {
      table.getCellEditor().stopCellEditing();
    }
  }
});

frame.addWindowStateListener(new WindowStateListener() {
  @Override public void windowStateChanged(WindowEvent e) {
    if (frame.getExtendedState() == JFrame.MAXIMIZED_BOTH && table.isEditing()) {
      table.getCellEditor().stopCellEditing();
    }
  }
});

参考リンク

コメント

  • どわー。助かりましたっ! -- shun
    • お役に立てて何よりです(自分もこの辺りよく混乱します)。 -- aterai
  • columnMarginChanged、役に立ちました。ありがとうございます。 -- はじめ
    • どうもです。JTable#columnMarginChangedメソッドのJavaDocの説明では、"If a cell is being edited, then editing is stopped and the cell is redrawn."となっているので、JTable#editingStoppedメソッドを使っている(現在の文字列が新しい値となる)のかなと思ったら、実際は、JTable#editingCanceledメソッド(=JTable#removeEditorメソッド)でキャンセルしている(編集内容は適用されない)ので、ちょっと混乱してしまいます。 -- aterai
    • 1.7.0では、Bug ID: 4330950 Lost newly entered data in the cell when resizing column width で、修正済みになっているのですが、getCellEditor().cancelCellEditing()を使うように変更されているので、編集が消えてしまうのは同じような・・・。 -- aterai