---
category: swing
folder: TableSortActionMap
title: JTableのソートをキー入力で実行する
tags: [JTable, JTableHeader, TableRowSorter, ActionMap, InputMap]
author: aterai
pubdate: 2025-01-20T02:05:16+09:00
description: JTableやJTableHeaderにフォーカスが存在する場合、マウスクリックではなくキー入力でソート可能になるよう設定します。
image: https://drive.google.com/uc?id=1kqcmLvCkScEmE0J8tuGG14Kasonb3XOZ
---
* Summary [#summary]
`JTable`や`JTableHeader`にフォーカスが存在する場合、マウスクリックではなくキー入力でソート可能になるよう設定します。

#download(https://drive.google.com/uc?id=1kqcmLvCkScEmE0J8tuGG14Kasonb3XOZ)

* Source Code Examples [#sourcecode]
#code(link){{
private void initMap(JTable table, String key, String ks, SortOrder order) {
  Action a = new AbstractAction() {
    @Override public void actionPerformed(ActionEvent e) {
      columnSort(e, order);
    }
  };
  table.getActionMap().put(key, a);
  InputMap im1 = table.getInputMap(WHEN_FOCUSED);
  im1.put(KeyStroke.getKeyStroke(ks), key);
  JTableHeader header = table.getTableHeader();
  header.getActionMap().put(key, a);
  InputMap im2 = header.getInputMap(WHEN_FOCUSED);
  im2.put(KeyStroke.getKeyStroke(ks), key);
}

private void columnSort(ActionEvent e, SortOrder order) {
  JTable table = null;
  int col = -1;
private static void columnSort(ActionEvent e, SortOrder order) {
  Object o = e.getSource();
  if (o instanceof JTable) {
    table = (JTable) o;
    JTable table = (JTable) o;
    JTableHeader header = table.getTableHeader();
    if (header != null) {
      table.getActionMap().get("focusHeader").actionPerformed(e);
      col = table.getSelectedColumn();
      int col = table.getSelectedColumn();
      sort(table, col, order);
      int id = ActionEvent.ACTION_PERFORMED;
      String cmd = "focusTable";
      ActionEvent ae = new ActionEvent(header, id, cmd);
      header.getActionMap().get(cmd).actionPerformed(ae);
    }
  } else if (o instanceof JTableHeader) {
    JTableHeader header = (JTableHeader) o;
    table = header.getTable();
    col = getSelectedColumnIndex(header);
    JTable table = header.getTable();
    int col = getSelectedColumnIndex(header);
    sort(table, col, order);
  }
  if (col >= 0 && table != null) {
}

private static void sort(JTable table, int col, SortOrder order) {
  if (col >= 0) {
    RowSorter.SortKey sortKey = new RowSorter.SortKey(col, order);
    table.getRowSorter().setSortKeys(Collections.singletonList(sortKey));
  }
}

private int getSelectedColumnIndex(JTableHeader header) {
  int col = -1;
  TableColumnModel cm = header.getColumnModel();
  for (int i = 0; i < cm.getColumnCount(); i++) {
    TableCellRenderer r = cm.getColumn(i).getHeaderRenderer();
    if (r instanceof ColumnHeaderRenderer) {
      col = ((ColumnHeaderRenderer) r).getSelectedColumnIndex();
      if (col >= 0) {
        break;
      }
    }
  }
  return col;
}
}}

* Explanation [#explanation]
- `JTableHeader`にフォーカスが存在する場合KBD{SPACE}キーで`toggleSortOrder`アクションが実行されてソートが可能だが、方向指定したソートは不可能
-- `JTable`にフォーカスが存在する場合は、KBD{F8}キーで`focusHeader`アクションを実行して`JTableHeader`にフォーカス移動してから`toggleSortOrder`アクションを実行する必要がある
- `JTable`と`JTableHeader`の`ActionMap`、`InputMap`にそれぞれ以下のソート`Action`と`KeyStroke`を追加
-- `ascendant`アクション、KBD{ctrl+↑}キー(`KeyStroke.getKeyStroke("ctrl UP")`)
-- `descendant`アクション、KBD{ctrl+↓}キー(`KeyStroke.getKeyStroke("ctrl DOWN")`)
-- `unsorted`アクション、KBD{F9}キー(`KeyStroke.getKeyStroke("F9")`)
- ソート`Action`はソート方向を保持し`JTable`、`JTableHeader`のどちらでイベントが実行されたかを判断してソートを実行する
-- `JTable`にフォーカスが存在する状態の場合、`focusHeader`アクションを実行して`JTableHeader`にフォーカス移動してから`JTable#getSelectedColumn()`で取得した列でソート
-- `JTable`にフォーカスが存在する状態の場合、`focusHeader`アクションを実行して`JTableHeader`にフォーカス移動してから`JTable#getSelectedColumn()`で取得した列でソートし、その後`JTableHeader`から`focusTable`アクションを取得、実行して`JTable`にフォーカスを戻す
-- `JTableHeader`にフォーカスが存在する状態の場合、現在選択されている列を取得する必要があるが`BasicTableHeaderUI#getSelectedColumnIndex()`メソッドは`private`で使用不可能のため、フォーカスが存在する`TableColumn`のインデックスを保持するだけの`TableCellRenderer`を`TableColumn#setHeaderRenderer(...)`ですべての列に設定
--- `TableCellRenderer#getTableCellRendererComponent(...)`以下のようにフォーカスが存在する場合の列インデックスを保持する(選択状態`isSelected`は列選択モデルでは常に`-1`で使用不可能)

#code{{
class ColumnHeaderRenderer implements TableCellRenderer {
  private int selectedIndex = -1;

  @Override public Component getTableCellRendererComponent(
      JTable table, Object value, boolean isSelected, boolean hasFocus,
      int row, int column) {
    if (hasFocus) {
      selectedIndex = column;
    }
    TableCellRenderer r = table.getTableHeader().getDefaultRenderer();
    return r.getTableCellRendererComponent(
        table, value, isSelected, hasFocus, row, column);
  }

  public int getSelectedColumnIndex() {
    return selectedIndex;
  }
}
}}

* Reference [#reference]
- [[JTableがデフォルトでソートする列を設定する>Swing/DefaultSortingColumn]]

* Comment [#comment]
#comment
#comment