JTableHeaderのソートアイコンをヘッダセルの左上に表示する
Total: 891
, Today: 1
, Yesterday: 2
Posted by aterai at
Last-modified:
概要
JTableHeader
の任意の列のソートアイコンをtable
タグを使用してヘッダセルの左上に表示するよう設定します。
Screenshot
Advertisement
サンプルコード
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;
}
}
View in GitHub: Java, Kotlin解説
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'>
のようなタグを生成して使用している