概要

JTableHeaderをタブエリアとして使用し、TableColumnをクリックするとCardLayoutのコンテナが切り替わるようにマウスリスナーを設定します。

サンプルコード

class TableHeaderTabbedPane extends JPanel {
  protected final CardLayout cardLayout = new CardLayout();
  protected final JPanel tabPanel = new JPanel(new GridLayout(1, 0, 0, 0));
  protected final JPanel contentsPanel = new JPanel(cardLayout);
  protected final TableColumnModel model;
  private final JTableHeader header;
  private Object selectedColumn;
  private int rolloverColumn = -1;

  public TableHeaderTabbedPane() {
    super(new BorderLayout());
    int left  = 1;
    int right = 3;
    tabPanel.setBorder(BorderFactory.createEmptyBorder(1, left, 0, right));
    contentsPanel.setBorder(BorderFactory.createEmptyBorder(4, left, 2, right));

    JTable table = new JTable(new DefaultTableModel(null, new String[] {}));
    // table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
    header = table.getTableHeader();
    model = (TableColumnModel) header.getColumnModel();

    MouseAdapter handler = new MouseInputHandler();
    header.addMouseListener(handler);
    header.addMouseMotionListener(handler);

    final TabButton l = new TabButton();
    header.setDefaultRenderer(new TableCellRenderer() {
      @Override public Component getTableCellRendererComponent(
          JTable tbl, Object val, boolean isS, boolean hasF, int row, int col) {
        l.setText((String) val);
        l.setSelected(Objects.equals(val, selectedColumn)
                   || Objects.equals(col, rolloverColumn));
        return l;
      }
    });

    JScrollPane sp = new JScrollPane();
    JViewport vp = new JViewport() {
      @Override public Dimension getPreferredSize() {
        return new Dimension();
      }
    };
    vp.setView(table);
    sp.setViewport(vp);
    // JPanel wrapPanel = new JPanel(new BorderLayout(0, 0));
    // wrapPanel.add(sp);
    // add(wrapPanel, BorderLayout.NORTH);
    add(sp, BorderLayout.NORTH);
    add(contentsPanel);
  }

  public void addTab(final String title, final Component comp) {
    contentsPanel.add(comp, title);
    TableColumn tc = new TableColumn(
        model.getColumnCount(), 75, header.getDefaultRenderer(), null);
    tc.setHeaderValue(title);
    model.addColumn(tc);
    if (Objects.isNull(selectedColumn)) {
      cardLayout.show(contentsPanel, title);
      selectedColumn = title;
    }
  }

  private class MouseInputHandler extends MouseAdapter {
    @Override public void mousePressed(MouseEvent e) {
      JTableHeader header = (JTableHeader) e.getComponent();
      int index = header.columnAtPoint(e.getPoint());
      if (index < 0) {
        return;
      }
      Object title = model.getColumn(index).getHeaderValue();
      cardLayout.show(contentsPanel, (String) title);
      selectedColumn = title;
    }

    @Override public void mouseEntered(MouseEvent e) {
      updateRolloverColumn(e);
    }

    @Override public void mouseMoved(MouseEvent e) {
      updateRolloverColumn(e);
    }

    @Override public void mouseDragged(MouseEvent e) {
      rolloverColumn = -1;
      updateRolloverColumn(e);
    }

    @Override public void mouseExited(MouseEvent e) {
      // int oldRolloverColumn = rolloverColumn;
      rolloverColumn = -1;
    }

    // @see BasicTableHeaderUI.MouseInputHandler
    private void updateRolloverColumn(MouseEvent e) {
      if (Objects.isNull(header.getDraggedColumn())
          && header.contains(e.getPoint())) {
        int col = header.columnAtPoint(e.getPoint());
        if (col != rolloverColumn) {
          // int oldRolloverColumn = rolloverColumn;
          rolloverColumn = col;
          // rolloverColumnUpdated(oldRolloverColumn, rolloverColumn);
        }
      }
    }
  }
}
View in GitHub: Java, Kotlin

解説

このサンプルでは、CardLayoutJTableHeaderを使用してコンテナの切り替えを実行するJTabbedPane風のコンポーネントを作成しています。

  • JTableHeaderは空のJTableを作成してそこから取得
    • new JTableHeader(TableColumnModel cm)より簡単なため
  • JScrollPaneにサイズ0JViewportを設定し上記のJTableを追加
    • 直接JTableHeaderを追加するとうまくリサイズできない?
  • JTableHeaderMouseListenerを追加し、TableColumn(タブとして代用)がクリックされるとそのタイトル文字列を引数にしてCardLayout#show(...)を実行してコンテナの切り替えを行う
  • JTableHeaderデフォルト機能であるドラッグ&ドロップによる入れ替えやヘッダ(タブ)幅のリサイズが利用可能

参考リンク

コメント