Swing/SortIconLayoutHeaderRenderer の変更点
- 追加された行はこの色です。
- 削除された行はこの色です。
- Swing/SortIconLayoutHeaderRenderer へ行く。
- Swing/SortIconLayoutHeaderRenderer の差分を削除
--- 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] `JTableHeader`の任意の列のソートアイコンを`table`タグを使用してヘッダセルの左上に表示するよう設定します。 #download(https://drive.google.com/uc?id=1f31_xhhURzzecQrFMY_jHDhCwvXngzFa) * サンプルコード [#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; } } }} * 解説 [#explanation] - `JTable#getColumnModel().getColumn(index)#setHeaderRenderer(...)`で`index`列目のヘッダレンダラーをラップし、ヘッダ文字列内に`html`タグを使用してソートアイコンを描画するよう設定 - `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] - [[JTableのソートアイコンを変更>Swing/TableSortIcon]] - [[JTableHeaderのソートアイコンをヘッダセル右揃えで表示する>Swing/TableHeaderRightAlignSortArrow]] * コメント [#comment] #comment #comment