• 追加された行はこの色です。
  • 削除された行はこの色です。
TITLE:JTableのセルをエクスプローラ風に表示する
#navi(../)
*JTableのセルをエクスプローラ風に表示する [#uc74456c]
>編集者:[[Terai Atsuhiro>terai]]~
作成日:2006-07-18~
更新日:&lastmod;
---
category: swing
folder: ExplorerLikeTable
title: JTableのセルをエクスプローラ風に表示する
tags: [JTable, TableCellRenderer, JLabel, Focus]
author: aterai
pubdate: 2006-07-17T08:23:31+09:00
description: セルの中にアイコンと文字列を配置し、エクスプローラ風に見えるよう、文字列だけにフォーカスをかけます。
image: https://lh3.googleusercontent.com/_9Z4BYR88imo/TQTMWzLLVKI/AAAAAAAAAZA/k3vF14Jt-V0/s800/ExplorerLikeTable.png
---
* 概要 [#summary]
セルの中にアイコンと文字列を配置し、エクスプローラ風に見えるよう、文字列だけにフォーカスをかけます。

#contents
#download(https://lh3.googleusercontent.com/_9Z4BYR88imo/TQTMWzLLVKI/AAAAAAAAAZA/k3vF14Jt-V0/s800/ExplorerLikeTable.png)

**概要 [#z459abad]
セルの中にアイコンと文字列を配置し、エクスプローラ風に見えるよう、文字列だけにフォーカスをかけます。
* サンプルコード [#sourcecode]
#code(link){{
class TestRenderer extends Box implements TableCellRenderer {
  private final Border emptyBorder = BorderFactory.createEmptyBorder(1, 1, 1, 1);
  private final ImageIcon nicon;
  private final ImageIcon sicon;
  private final JLabel textLabel;
  private final JLabel iconLabel;
  public TestRenderer(JTable table) {
    super(BoxLayout.X_AXIS);
    textLabel = new JLabel("JLabel");
    textLabel.setOpaque(true);
    textLabel.setBorder(emptyBorder);
    nicon = new ImageIcon(getClass().getResource("wi0063-16.png"));
    FilteredImageSource fis = new FilteredImageSource(
      nicon.getImage().getSource(), new SelectedImageFilter());
    sicon = new ImageIcon(createImage(fis));
    iconLabel = new JLabel(nicon);
    iconLabel.setBorder(BorderFactory.createEmptyBorder());
    table.setRowHeight(Math.max(textLabel.getPreferredSize().height,
                  iconLabel.getPreferredSize().height));
    add(iconLabel);
    add(textLabel);
    add(Box.createHorizontalGlue());
  }

#screenshot
  @Override public Component getTableCellRendererComponent(JTable table,
      Object value, boolean isSelected, boolean hasFocus, int row, int column) {
    textLabel.setText(Objects.toString(value, ""));
    FontMetrics fm = table.getFontMetrics(table.getFont());
    int swidth = fm.stringWidth(textLabel.getText()) + textLabel.getInsets().left
                              + textLabel.getInsets().right;
    int cwidth = table.getColumnModel().getColumn(column).getWidth()
                   -iconLabel.getPreferredSize().width;
    textLabel.setPreferredSize(new Dimension(swidth > cwidth ? cwidth : swidth, 0));
    if (isSelected) {
      textLabel.setForeground(table.getSelectionForeground());
      textLabel.setBackground(table.getSelectionBackground());
    } else {
      textLabel.setForeground(table.getForeground());
      textLabel.setBackground(table.getBackground());
    }
    if (hasFocus) {
      textLabel.setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
    } else {
      textLabel.setBorder(emptyBorder);
    }
    textLabel.setFont(table.getFont());
    iconLabel.setIcon(isSelected?sicon:nicon);
    return this;
  }

**サンプルコード [#f06fb511]
 public TestRenderer(JTable table) {
   super(BoxLayout.X_AXIS);
   label.setBorder(BorderFactory.createEmptyBorder(0,2,0,0));
   nicon = new ImageIcon(getClass().getResource("wi0063-16.png"));
   FilteredImageSource fis = new FilteredImageSource(
                 nicon.getImage().getSource(),
                 new SelectedImageFilter());
   sicon = new ImageIcon(createImage(fis));
   iconLabel.setIcon(nicon);
   textLabel = new MyLabel(table);
   table.setRowHeight(textLabel.getPreferredSize().height);
 }
 public Component getTableCellRendererComponent(
                  JTable table, Object value, boolean isSelected,
                  boolean hasFocus, int row, int column) {
   textLabel.setText((value ==null) ? "" : value.toString());
   FontMetrics fm = table.getFontMetrics(table.getFont());
   int swidth = fm.stringWidth(textLabel.getText()) +
                textLabel.getInsets().left +
                textLabel.getInsets().right;
   int cwidth = table.getColumnModel().getColumn(column).getWidth() -
                iconLabel.getPreferredSize().width;
   textLabel.setPreferredSize(
     new Dimension((swidth>cwidth)?cwidth:swidth, 10000));
   textLabel.setSelected(isSelected);
   textLabel.setFocusedBorder(hasFocus);
   textLabel.setFont(table.getFont());
   iconLabel.setIcon((isSelected)?sicon:nicon);
   removeAll();
   add(iconLabel);
   add(textLabel);
   add(Box.createHorizontalGlue());
   return this;
 }
  private static class SelectedImageFilter extends RGBImageFilter {
    public SelectedImageFilter() {
      canFilterIndexColorModel = false;
    }

-&jnlp;
-&jar;
-&zip;
    @Override public int filterRGB(int x, int y, int argb) {
      // Color color = new Color(argb, true);
      // float[] array = new float[4];
      // color.getComponents(array);
      // return new Color(array[0] * .5f, array[1] * .5f, array[2], array[3]).getRGB();
      int r = (argb >> 16) & 0xFF;
      int g = (argb >> 8) & 0xFF;
      return (argb & 0xFF_00_00_FF) | ((r >> 1) << 16) | ((g >> 1) << 8);
    }
  }
}
}}

**解説 [#u6964aab]
最初の列などをセルレンダラーを使って、エクスプローラの詳細のような表示にしています。
* 解説 [#explanation]
`Windows Explorer`(ファイルシステム・エクスプローラ)の詳細表示風にするため、以下のような設定をしています。

レンダラーでは、アイコンと文字列を別々のJLabelで作成して並べることで、フォーカスはセルではなく文字列だけに点線で掛かるようになっています。
- セル間の罫線を非表示(`JTable#setShowHorizontalLines`、`JTable#setShowVerticalLines`)
- ひとつのセルの中でアイコンと文字列を表示するセルレンダラーを作成
- フォーカスが`JTable`に無い場合選択されたセルの背景色をパネルの背景色に変更

また、選択時にはセル全体の背景色を変更するのではなく、文字列を表示しているJLabelの背景色を変更し、そのPreferredSizeを文字列の長さまで縮小して右側に余白を作成しています。
このレンダラーではアイコンと文字列を別々の`JLabel`で作成して並べることで`JList#putClientProperty("List.isFileList", Boolean.TRUE)`した場合のようにフォーカスはセルではなく文字列だけに点線で掛かるようになっています。

-エクスプローラ風になっていない点
--アイコンと文字列以外の場所(セル内)をクリックしても、選択できてしまう
---[[JTableで文字列をクリックした場合だけセルを選択状態にする>Swing/CellAtPoint]]
---WindowsLaFでのJFileChooserはどうやっているのだろうか?
--マウスによる範囲指定で選択することができない
---[[JListのアイテムを範囲指定で選択>Swing/RubberBanding]]
--ソートすると選択状態がクリアされてしまう
---[[TableSorterでソートしても選択状態を維持>Swing/SelectionKeeper]]
選択時にはセル全体の背景色を変更するのではなく文字列を表示している`JLabel`の背景色を変更しその`PreferredSize`を文字列の長さまで縮小して右側に余白を作成しています。

**参考リンク [#qe04acfe]
-[[XP Style Icons - Windows Application Icon, Software XP Icons>http://www.icongalore.com/]]
--アイコンを利用しています。
- `Windows Explorer`との相違点
-- アイコンと文字列以外の場所(セル内)をクリックしても選択できてしまう
--- `WindowsLookAndFeel`での`JFileChooser`は`JTable.putClientProperty("Table.isFileList", Boolean.TRUE)`を使用?
--- [[JTableで文字列をクリックした場合だけセルを選択状態にする>Swing/TableFileList]]
-- マウスドラッグによる範囲指定で選択できない
--- [[JTableで文字列をクリックした場合だけセルを選択状態にする>Swing/TableFileList]]
--- [[JListのアイテムを範囲指定で選択>Swing/RubberBanding]]
-- %%ソートすると選択状態がクリアされてしまう%%
--- `TableRowSorter`では標準で選択状態を維持するようになった
--- [[TableSorterでソートしても選択状態を維持>Swing/SelectionKeeper]]
-- %%KBD{Tab}キー、矢印キーによる選択状態の移動がおかしい%%
-- 編集不可

**コメント [#ld0c329a]
* 参考リンク [#reference]
- [https://xp-style-icons.en.softonic.com/ XP Style Icons - Download]
- [[JTableで文字列をクリックした場合だけセルを選択状態にする>Swing/TableFileList]]

* コメント [#comment]
#comment
#comment