概要

JTableHeaderのカラムセルを描画するセルレンダラーとしてJButtonを適用します。

サンプルコード

class ButtonHeaderRenderer extends JButton implements TableCellRenderer {
  private int pushedColumn = -1;
  private int rolloverColumn = -1;

  @Override public void updateUI() {
    super.updateUI();
    setHorizontalTextPosition(LEFT);
  }

  @Override public Component getTableCellRendererComponent(
      JTable table, Object value,
      boolean isSelected, boolean hasFocus,
      int row, int column) {
    setText(Objects.toString(value, ""));
    int modelColumn = table.convertColumnIndexToModel(column);
    JTableHeader header = table.getTableHeader();
    if (header != null) {
      // setColor(header, hasFocus);
      boolean isPressed = modelColumn == pressedColumn;
      getModel().setPressed(isPressed);
      getModel().setArmed(isPressed);
      getModel().setRollover(modelColumn == rolloverColumn);
      setFont(header.getFont());
    }

    Icon sortIcon = null;
    if (table.getRowSorter() != null) {
      List<? extends RowSorter.SortKey> sortKeys =
          table.getRowSorter().getSortKeys();
      if (!sortKeys.isEmpty() &&
          sortKeys.get(0).getColumn() == modelColumn) {
        SortOrder sortOrder = sortKeys.get(0).getSortOrder();
        switch (sortOrder) {
          case ASCENDING:
            sortIcon = UIManager.getIcon("Table.ascendingSortIcon");
            break;
          case DESCENDING:
            sortIcon = UIManager.getIcon("Table.descendingSortIcon");
            break;
          // case UNSORTED:
          //   sortIcon = UIManager.getIcon("Table.naturalSortIcon");
          //   break;
          default:
            sortIcon = UIManager.getIcon("Table.naturalSortIcon");
        }
      }
    }
    setIcon(sortIcon);
    return this;
  }

  public void setPressedColumn(int column) {
    pushedColumn = column;
  }

  public void setRolloverColumn(int column) {
    rolloverColumn = column;
  }
}

class HeaderMouseListener extends MouseAdapter {
  @Override public void mousePressed(MouseEvent e) {
    JTableHeader header = (JTableHeader) e.getComponent();
    JTable table = header.getTable();
    TableCellRenderer renderer = header.getDefaultRenderer();
    int viewColumn = table.columnAtPoint(e.getPoint());
    if (viewColumn >= 0 && renderer instanceof ButtonHeaderRenderer) {
      int column = table.convertColumnIndexToModel(viewColumn);
      ((ButtonHeaderRenderer) renderer).setPressedColumn(column);
    }
  }

  @Override public void mouseReleased(MouseEvent e) {
    JTableHeader header = (JTableHeader) e.getComponent();
    TableCellRenderer renderer = header.getDefaultRenderer();
    if (renderer instanceof ButtonHeaderRenderer) {
      ((ButtonHeaderRenderer) renderer).setPressedColumn(-1);
    }
  }

  @Override public void mouseMoved(MouseEvent e) {
    JTableHeader header = (JTableHeader) e.getComponent();
    JTable table = header.getTable();
    TableCellRenderer renderer = header.getDefaultRenderer();
    int viewColumn = table.columnAtPoint(e.getPoint());
    if (viewColumn >= 0 && renderer instanceof ButtonHeaderRenderer) {
      int column = table.convertColumnIndexToModel(viewColumn);
      ((ButtonHeaderRenderer) renderer).setRolloverColumn(column);
    }
  }

  @Override public void mouseExited(MouseEvent e) {
    JTableHeader header = (JTableHeader) e.getComponent();
    TableCellRenderer renderer = header.getDefaultRenderer();
    if (renderer instanceof ButtonHeaderRenderer) {
      ((ButtonHeaderRenderer) renderer).setRolloverColumn(-1);
    }
  }
}
View in GitHub: Java, Kotlin

解説

  • 上:
    • JTableHeaderのデフォルトセルレンダラーはsun.swing.table.DefaultTableCellHeaderRendererJLabelでセルを描画
    • DefaultTableCellRendererをそのまま使用しないのは、ソートアイコン描画をJava 6から追加したため?
  • 下:
    • JButtonを継承するセルレンダラーを作成してJTableHeader#setDefaultRenderer(...)で設定
    • JButton#setHorizontalTextPosition(LEFT)を設定してソートアイコンをテキストの右側に表示するよう設定
    • クリック時などのセル描画をJButton#setForeground(...)JButton#setBackground(...)ではなくButtonModel#setSelected(...)ButtonModel#setPressed(...)ButtonModel#setArmed(...)で変更
    • ロールオーバー状態やプレス状態はTableCellRenderer#getTableCellRendererComponent(...)内で取得できないので、JTableHeaderMouseListenerMouseMotionListenerを追加して現在ロールオーバー状態やプレス状態を描画する列を記憶している
    • MotifLookAndFeelに切り替えるとJTableHeaderのデフォルトカーソルが列幅リサイズカーソルに固定されてしまう場合がある?
      • JTable#updateUI()をオーバーライドし、JTableHeader#setCursor(Cursor.getDefaultCursor())を実行して回避

参考リンク

コメント