• category: swing folder: AddRemoveTableColumn title: JTableHeaderに追加された各TableColumnの表示・非表示を切り替える tags: [JTable, JTableHeader, TableColumn, TableColumnModel, JPopupMenu, JCheckBoxMenuItem] author: aterai pubdate: 2019-12-16T16:53:12+09:00 description: JTableHeaderのTableColumnが表示・非表示状態かをJCheckBoxMenuItemを使用して切り替えます。 image: https://drive.google.com/uc?id=1s7b9r9oP8o0El5Ti3e1xM84anoj5r_Te hreflang:
       href: https://java-swing-tips.blogspot.com/2020/09/creating-statusbar-with-size-grips-to.html
       lang: en

概要

JTableHeaderTableColumnが表示・非表示状態かをJCheckBoxMenuItemを使用して切り替えます。

サンプルコード

class TableHeaderPopupMenu extends JPopupMenu {
  protected TableHeaderPopupMenu(JTable table) {
    super();
    TableColumnModel columnModel = table.getColumnModel();
    List<TableColumn> list = Collections.list(columnModel.getColumns());
    list.forEach(tableColumn -> {
      String name = Objects.toString(tableColumn.getHeaderValue());
      // System.out.format("%s - %s%n", name, tableColumn.getIdentifier());
      JCheckBoxMenuItem item = new JCheckBoxMenuItem(name, true);
      item.addItemListener(e -> {
        if (((AbstractButton) e.getItemSelectable()).isSelected()) {
          columnModel.addColumn(tableColumn);
        } else {
          columnModel.removeColumn(tableColumn);
        }
        updateMenuItems(columnModel);
      });
      add(item);
    });
  }

  @Override public void show(Component c, int x, int y) {
    if (c instanceof JTableHeader) {
      JTableHeader header = (JTableHeader) c;
      JTable table = header.getTable();
      header.setDraggedColumn(null);
      header.repaint();
      table.repaint();
      updateMenuItems(header.getColumnModel());
      super.show(c, x, y);
    }
  }

  private void updateMenuItems(TableColumnModel columnModel) {
    boolean isOnlyOneMenu = columnModel.getColumnCount() == 1;
    if (isOnlyOneMenu) {
      stream(this).map(MenuElement::getComponent).forEach(mi ->
          mi.setEnabled(!(mi instanceof AbstractButton)
                        || !((AbstractButton) mi).isSelected()));
    } else {
      stream(this).forEach(me -> me.getComponent().setEnabled(true));
    }
  }

  private static Stream<MenuElement> stream(MenuElement me) {
    return Stream.of(me.getSubElements())
      .flatMap(m -> Stream.concat(Stream.of(m), stream(m)));
  }
}
View in GitHub: Java, Kotlin

解説

  • 初期状態ではTableModelから生成されたTableColumnがすべて表示されている
    • JCheckBoxMenuItemもすべて選択状態になる
  • JCheckBoxMenuItemで選択解除されたらTableColumnModel#removeColumn(TableColumn)メソッドでTableColumnを非表示
    • TableColumnModelから列は削除されてJTableHeaderからは非表示になるが、TableModelのその列はそのまま残っている
    • TableColumnがすべて非表示にならないよう、JPopupMenuを開くときなどにその列数をチェックしてJCheckBoxMenuItemの選択可・不可を切り替えている
  • JCheckBoxMenuItemで選択設定されたらTableColumnModel#addColumn(TableColumn)メソッドでTableColumnを表示
    • TableColumnModelに列は追加されてJTableHeaderにも表示されるが、TableModelは初期状態から変化しない

  • Java 9以上でUIManager.put("CheckBoxMenuItem.doNotCloseOnMouseClick", true);を設定し、JPopupMenuを開いたまま現在選択状態のTableColumnJCheckBoxMenuItemで非表示に切り替えるとArrayIndexOutOfBoundsExceptionが発生する
    • PopupMenuListenerJPopupMenuに追加、またはJPopupMenu#show(...)メソッドをオーバーライドしてJTableHeader.setDraggedColumn(null);で選択状態をクリアすることで回避可能

参考リンク

コメント