TITLE:JTreeの行選択し、背景色を変更
Posted by aterai at 2011-01-17

JTreeの行選択し、背景色を変更

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

概要

JTreeの行をクリックして選択し、行全体を選択状態の背景色で描画します。
TreeRowSelection.png

サンプルコード

#spanend
#spanadd
class RowSelectionTree extends JTree {
#spanend
  private static final Color SELC = new Color(100, 150, 200);
  private Handler handler;

#spandel
**サンプルコード [#adcacb20]
#spanend
#spandel
#code{{
#spanend
#spandel
final Color SELC = new Color(100,150,200);
#spanend
#spandel
JTree tree = new JTree() {
#spanend
  @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);
    }
  }
#spandel
};
#spanend
#spandel
tree.setUI(new javax.swing.plaf.basic.BasicTreeUI() {
#spanend
  @Override public Rectangle getPathBounds(JTree tree, TreePath path) {
    if(tree != null && treeState != null) {
      return getPathBounds(path, tree.getInsets(), new Rectangle());
    }
    return null;
#spanadd

#spanend
  @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;
      }
#spanadd

#spanend
      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;
#spanadd

#spanend
  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;
#spanadd

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

#spanend
    @Override public void focusLost(FocusEvent e) {
      e.getComponent().repaint();
    }
  }
#spandel
});
#spanend
#spandel
tree.setOpaque(false);
#spanend
#spanadd
}
#spanend
View in GitHub: Java, Kotlin

解説

以下のような設定で、JTreeを行選択できるように変更し、表示もNimbus Look&Feel風に描画するようにしています。

解説

  • 左: デフォルト
    • MetalLookAndFeelなどでは選択でノードの背景色が変化
  • 右: JTreeノードを行選択に変更
    • NimbusLookAndFeel風に行全体を選択状態の背景色で描画
    • BasicTreeUI#getPathBounds(...)をオーバーライドしてノードではなく行のクリックで選択可能に変更
    • JTreeの背景をsetOpaque(false)で透明(非描画)に設定しJTree#paintComponent(...)をオーバーライドして選択された行を背景色で描画
    • 不透明にしたTreeCellRendererを使用してノードの選択色をJTree#paintComponent(...)の背景色と同じものに変更
    • 別コンポーネントにフォーカスが移動した場合LeadSelectionBorderを描画しない(選択背景色で上書き)ように設定
      • デフォルトではノードのみ再描画されるのでFocusListenerを追加してJTree全体を再描画
      • UIManager.put("Tree.repaintWholeRow", Boolean.TRUE)を設定することでも回避可能
  • BasicTreeUI#getPathBounds(...)をオーバーライドして、ノードではなく、行のクリックで選択可能に変更
  • JTreeをsetOpaque(false)で透明にし、JTree#paintComponent(...)をオーバーライドして選択された行を背景色で描画
  • 不透明にしたTreeCellRendererを使用して、ノードの選択色をJTree#paintComponent(...)の背景色と同じものに変更

参考リンク

コメント

コメント