Swing/TreeRowSelection の変更点
- 追加された行はこの色です。
- 削除された行はこの色です。
- Swing/TreeRowSelection へ行く。
- Swing/TreeRowSelection の差分を削除
--- 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 [#summary] `JTree`の行をクリックして選択し、行全体を選択状態の背景色で描画します。 #download(https://lh6.googleusercontent.com/_9Z4BYR88imo/TTPdCvaUyfI/AAAAAAAAAyQ/QnF4vHjyUiM/s800/TreeRowSelection.png) * Source Code Examples [#sourcecode] #code(link){{ class RowSelectionTree extends JTree { private static final Color SELC = new Color(100, 150, 200); private Handler handler; @Override protected void paintComponent(Graphics g) { g.setColor(getBackground()); g.fillRect(0, 0, getWidth(), getHeight()); if (getSelectionCount() > 0) { g.setColor(SELC); for (int i : getSelectionRows()) { Rectangle r = getRowBounds(i); g.fillRect(0, r.y, getWidth(), r.height); } } super.paintComponent(g); if (getLeadSelectionPath() != null) { Rectangle r = getRowBounds(getRowForPath(getLeadSelectionPath())); g.setColor(hasFocus() ? SELC.darker() : SELC); g.drawRect(0, r.y, getWidth() - 1, r.height - 1); } } @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); } 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; } @Override public void focusGained(FocusEvent e) { e.getComponent().repaint(); } @Override public void focusLost(FocusEvent e) { e.getComponent().repaint(); } } } }} * Explanation [#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)`を設定することでも回避可能 * Reference [#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]] * コメント [#comment] * Comment [#comment] #comment #comment