• 追加された行はこの色です。
  • 削除された行はこの色です。
TITLE:JTreeを行クリックで選択し、行全体を選択状態の背景色で描画
#navi(../)
#tags(JTree, TreeCellRenderer)
RIGHT:Posted by &author(aterai); at 2011-01-17
*JTreeを行クリックで選択し、行全体を選択状態の背景色で描画 [#w37a10e2]
``JTree``の行をクリックして選択し、行全体を選択状態の背景色で描画します。
---
category: swing
folder: TreeRowSelection
title: JTreeを行クリックで選択し、行全体を選択状態の背景色で描画
tags: [JTree, TreeCellRenderer, FocusListener]
author: aterai
pubdate: 2011-01-17T15:24:56+09:00
description: JTreeの行をクリックして選択し、行全体を選択状態の背景色で描画します。
image: https://lh6.googleusercontent.com/_9Z4BYR88imo/TTPdCvaUyfI/AAAAAAAAAyQ/QnF4vHjyUiM/s800/TreeRowSelection.png
hreflang:
    href: https://java-swing-tips.blogspot.com/2011/02/highlight-entire-jtree-row-on-selection.html
    lang: en
---
* 概要 [#summary]
`JTree`の行をクリックして選択し、行全体を選択状態の背景色で描画します。

-&jnlp;
-&jar;
-&zip;
#download(https://lh6.googleusercontent.com/_9Z4BYR88imo/TTPdCvaUyfI/AAAAAAAAAyQ/QnF4vHjyUiM/s800/TreeRowSelection.png)

//#screenshot
#ref(http://lh6.ggpht.com/_9Z4BYR88imo/TTPdCvaUyfI/AAAAAAAAAyQ/QnF4vHjyUiM/s800/TreeRowSelection.png)
* サンプルコード [#sourcecode]
#code(link){{
class RowSelectionTree extends JTree {
  private static final Color SELC = new Color(100, 150, 200);
  private Handler handler;

**サンプルコード [#n08c6886]
#code(link){{
final Color SELC = new Color(100,150,200);
JTree tree = new JTree() {
  @Override public void paintComponent(Graphics g) {
  @Override protected void paintComponent(Graphics g) {
    g.setColor(getBackground());
    g.fillRect(0,0,getWidth(),getHeight());
    if(getSelectionCount()>0) {
      for(int i: getSelectionRows()) {
    g.fillRect(0, 0, getWidth(), getHeight());
    if (getSelectionCount() > 0) {
      g.setColor(SELC);
      for (int i : getSelectionRows()) {
        Rectangle r = getRowBounds(i);
        g.setColor(SELC);
        g.fillRect(0, r.y, getWidth(), r.height);
      }
    }
    super.paintComponent(g);
    if(getLeadSelectionPath()!=null) {
    if (getLeadSelectionPath() != null) {
      Rectangle r = getRowBounds(getRowForPath(getLeadSelectionPath()));
      g.setColor(SELC.darker());
      g.drawRect(0, r.y, getWidth()-1, r.height-1);
      g.setColor(hasFocus() ? SELC.darker() : SELC);
      g.drawRect(0, r.y, getWidth() - 1, r.height - 1);
    }
  }
};
tree.setUI(new javax.swing.plaf.basic.BasicTreeUI() {
  @Override public Rectangle getPathBounds(JTree tree, TreePath path) {
    if(tree != null && treeState != null) {
      return getPathBounds(path, tree.getInsets(), new Rectangle());
    }
    return null;

  @Override public void updateUI() {
    removeFocusListener(handler);
    super.updateUI();
    setUI(new BasicTreeUI() {
      @Override public Rectangle getPathBounds(JTree tree, TreePath path) {
        if (tree != null && treeState != null) {
          return getPathBounds(path, tree.getInsets(), new Rectangle());
        }
        return null;
      }

      private Rectangle getPathBounds(
          TreePath path, Insets insets, Rectangle bounds) {
        Rectangle rect = treeState.getBounds(path, bounds);
        if (rect != null) {
          rect.width = tree.getWidth();
          rect.y += insets.top;
        }
        return rect;
      }
    });
    handler = new Handler();
    addFocusListener(handler);
    setCellRenderer(handler);
    setOpaque(false);
  }
  private Rectangle getPathBounds(TreePath path, Insets insets, Rectangle bounds) {
    bounds = treeState.getBounds(path, bounds);
    if(bounds != null) {
      bounds.width = tree.getWidth();
      bounds.y += insets.top;

  static class Handler extends DefaultTreeCellRenderer implements FocusListener {
    @Override public Component getTreeCellRendererComponent(
        JTree tree, Object value, boolean selected, boolean expanded,
        boolean leaf, int row, boolean hasFocus) {
      JLabel l = (JLabel) super.getTreeCellRendererComponent(
          tree, value, selected, expanded, leaf, row, hasFocus);
      l.setBackground(selected ? SELC : tree.getBackground());
      l.setOpaque(true);
      return l;
    }
    return bounds;

    @Override public void focusGained(FocusEvent e) {
      e.getComponent().repaint();
    }

    @Override public void focusLost(FocusEvent e) {
      e.getComponent().repaint();
    }
  }
});
tree.setOpaque(false);
}
}}

**解説 [#vd003856]
以下のような設定で、``JTree``を行選択できるように変更し、表示も``NimbusLookAndFeel``風に行全体を選択状態の背景色で描画するようにしています。
* 解説 [#explanation]
- 左: デフォルト
-- `MetalLookAndFeel`などでは選択でノードの背景色が変化
- 右: `JTree`ノードを行選択に変更
-- `NimbusLookAndFeel`風に行全体を選択状態の背景色で描画
-- `BasicTreeUI#getPathBounds(...)`をオーバーライドしてノードではなく行のクリックで選択可能に変更
--- ドキュメントの[https://docs.oracle.com/javase/jp/8/docs/api/javax/swing/plaf/basic/BasicTreeUI.html#getPathBounds-javax.swing.JTree-javax.swing.tree.TreePath- BasicTreeUI (Java Platform SE 8)]では「パスのコンポーネントのどれかが現在有効な場合は、`null`が返されます。(`Will return null if any component in path is currently valid.`)」となっているがコードは無効な場合`null`になる
--- [https://bugs.openjdk.org/browse/JDK-8281692 [JDK-8281692] javadoc for getPathBounds() in TreeUI and BasicTreeUI is incorrect - Java Bug System]によると`Java 19`で`Will return null if any component in path is currently invalid.`と修正された
-- `JTree`の背景を`setOpaque(false)`で透明(非描画)に設定し`JTree#paintComponent(...)`をオーバーライドして選択された行を背景色で描画
-- 不透明にした`TreeCellRenderer`を使用してノードの選択色を`JTree#paintComponent(...)`の背景色と同じものに変更
-- 別コンポーネントにフォーカスが移動した場合`LeadSelection`の`Border`を描画しない(選択背景色で上書き)ように設定
--- デフォルトではノードのみ再描画されるので`FocusListener`を追加して`JTree`全体を再描画
--- `UIManager.put("Tree.repaintWholeRow", Boolean.TRUE)`を設定することでも回避可能

- ``BasicTreeUI#getPathBounds(...)``をオーバーライドして、ノードではなく、行のクリックで選択可能に変更
- ``JTree``の背景を``setOpaque(false)``で透明(非描画)にし、``JTree#paintComponent(...)``をオーバーライドして選択された行を背景色で描画
- 不透明にした``TreeCellRenderer``を使用して、ノードの選択色を``JTree#paintComponent(...)``の背景色と同じものに変更
* 参考リンク [#reference]
- [http://krdlab.hatenablog.com/entry/20071209/1197143960 JTable でセルのないところに行っぽい表示を出せますか? - KrdLab's blog]
- [http://jroller.com/santhosh/entry/highlight_a_node_s_descendants Highlight a node's descendants in JTree - Santhosh Kumar's Weblog]
-- via: [https://community.oracle.com/thread/2160338 Swing - JTree - highlight entire row on selection]
- [[NimbusLookAndFeelでJTreeのセル選択を行全体ではなくノードに限定する>Swing/TreeCellSelectionBackground]]
-- こちらは逆に、`NimbusLookAndFeel`で行全体の選択を行わないようにする方法
- [https://bugs.openjdk.org/browse/JDK-8047749 [JDK-8047749] javadoc for getPathBounds() in TreeUI and BasicTreeUI is incorrect - Java Bug System]
- [https://bugs.openjdk.org/browse/JDK-8281692 [JDK-8281692] javadoc for getPathBounds() in TreeUI and BasicTreeUI is incorrect - Java Bug System]
- [[JTreeのノード選択可能な領域を行全体に拡張する>Swing/WholeRowSelectableTree]]

**参考リンク [#l98bff1d]
-[http://d.hatena.ne.jp/KrdLab/20071209/1197143960 JTable でセルのないところに行っぽい表示を出せますか? - KrdLabの不定期日記]
-[http://jroller.com/santhosh/entry/highlight_a_node_s_descendants Highlight a node's descendants in JTree - Santhosh Kumar's Weblog]
-- via: [http://forums.oracle.com/forums/thread.jspa?threadID=2158338 OTN Discussion Forums : JTree - highlight entire row on selection]

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