---
category: swing
folder: SortIconLayoutHeaderRenderer
title: JTableHeaderのソートアイコンをヘッダセルの左上に表示する
tags: [JTable, JTableHeader, TableCellRenderer, TableRowSorter]
author: aterai
pubdate: 2022-09-19T14:21:23+09:00
description: JTableHeaderの任意の列のソートアイコンをtableタグを使用してヘッダセルの左上に表示するよう設定します。
image: https://drive.google.com/uc?id=1f31_xhhURzzecQrFMY_jHDhCwvXngzFa
---
* Summary [#summary]
`JTableHeader`の任意の列のソートアイコンを`table`タグを使用してヘッダセルの左上に表示するよう設定します。
#download(https://drive.google.com/uc?id=1f31_xhhURzzecQrFMY_jHDhCwvXngzFa)
* Source Code Examples [#sourcecode]
#code(link){{
class SortIconLayoutHeaderRenderer implements TableCellRenderer {
private static final String ASCENDING = "Table.ascendingSortIcon";
private static final String DESCENDING = "Table.descendingSortIcon";
private final URI ascendingUri;
private final URI descendingUri;
private final URI naturalUri;
private final Icon ascendingIcon;
private final Icon descendingIcon;
private final EmptyIcon emptyIcon;
protected SortIconLayoutHeaderRenderer() {
ascendingIcon = UIManager.getLookAndFeelDefaults().getIcon(ASCENDING);
ascendingUri = getIconUri(ascendingIcon);
descendingIcon = UIManager.getLookAndFeelDefaults().getIcon(DESCENDING);
descendingUri = getIconUri(descendingIcon);
emptyIcon = new EmptyIcon();
naturalUri = getIconUri(emptyIcon);
}
@Override public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
emptyIcon.width = ascendingIcon.getIconWidth();
emptyIcon.height = ascendingIcon.getIconHeight();
UIManager.put(ASCENDING, emptyIcon);
UIManager.put(DESCENDING, emptyIcon);
TableCellRenderer r = table.getTableHeader().getDefaultRenderer();
Component c = r.getTableCellRendererComponent(
table, value, isSelected, hasFocus, row, column);
UIManager.put(ASCENDING, ascendingIcon);
UIManager.put(DESCENDING, descendingIcon);
if (c instanceof JLabel) {
JLabel l = (JLabel) c;
// l.setHorizontalAlignment(SwingConstants.RIGHT);
URI sortUri = null;
SortOrder sortOrder = getColumnSortOrder(table, column);
switch (sortOrder) {
case ASCENDING:
sortUri = ascendingUri;
break;
case DESCENDING:
sortUri = descendingUri;
break;
default: // case UNSORTED:
sortUri = naturalUri;
break;
}
int v = 10;
String img = String.format("<img src='%s'>", sortUri);
String pct = String.format("<td align='right'>%d%%", v);
String fmt = "<html><table><tr><td>%s%s<tr><td><td align='right'>%s";
l.setText(String.format(fmt, img, pct, Objects.toString(value, "")));
}
return c;
}
public static SortOrder getColumnSortOrder(JTable table, int column) {
SortOrder rv = SortOrder.UNSORTED;
if (table != null && table.getRowSorter() != null) {
List<? extends RowSorter.SortKey> sortKeys = table.getRowSorter().getSortKeys();
int mi = table.convertColumnIndexToModel(column);
if (!sortKeys.isEmpty() && sortKeys.get(0).getColumn() == mi) {
rv = sortKeys.get(0).getSortOrder();
}
}
return rv;
}
public static URI getIconUri(Icon icon) {
int w = icon.getIconWidth();
int h = icon.getIconHeight();
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
icon.paintIcon(null, g2, 0, 0);
g2.dispose();
try {
File tmp = File.createTempFile("icon", ".png");
tmp.deleteOnExit();
ImageIO.write(img, "png", tmp);
return tmp.toURI();
} catch (IOException ex) {
UIManager.getLookAndFeel().provideErrorFeedback(null);
ex.printStackTrace();
}
return null;
}
}
}}
* Description [#explanation]
* Description [#description]
- `JTable#getColumnModel().getColumn(index)#setHeaderRenderer(...)`で`index`列目のヘッダレンダラーをラップし、ヘッダ文字列内に`table`タグを使用して一行目の文字列の左にソートアイコンを描画するよう設定
- 元のソートアイコンは`JTable#getTableHeader()#getDefaultRenderer()#getTableCellRendererComponent(...)`でヘッダ描画用コンポーネントを取得する直前に空アイコンを`UIManager.put("Table.ascendingSortIcon", emptyIcon)`などで非表示に設定、ヘッダ描画用コンポーネントを取得後に`UIManager.put("Table.ascendingSortIcon", UIManager.getLookAndFeelDefaults().getIcon("Table.ascendingSortIcon"))`で`LookAndFeel`デフォルトのソートアイコンに復元
-- `TableCellRenderer#getTableCellRendererComponent(...)`内で上記の一時的なソートアイコンの置換を実行しないと指定した列以外のソートアイコンも非表示になる
-- `DefaultTableCellHeaderRenderer`を継承する`WindowsTableHeaderUI.XPDefaultRenderer`は`paint(...)`をオーバーライドしてヘッダセルを描画しているので`LayoutManager`を設定したり`JLayer`でアイコンを上書きするとフォーカス背景色などが描画されなくなる
- `html`タグで`Icon`を直接指定する方法がないので一旦ソートアイコンを`ImageIO.write(...)`で一時ファイルに書き出し、`File.toURI()`で`URI`に変換してから`<img src='file:/C:/Users/.../AppData/Local/Temp/icon1056940121138232542.png'>`のようなタグを生成して使用している
* Reference [#reference]
- [[JTableのソートアイコンを変更>Swing/TableSortIcon]]
- [[JTableHeaderのソートアイコンをヘッダセル右揃えで表示する>Swing/TableHeaderRightAlignSortArrow]]
* Comment [#comment]
#comment
#comment