Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Color row in JTree

I'd like to color elements in a JTree. However, simply adding a background color only to the label looks kind of strange. Particularly if more than one node is selected, the resulting shape looks ragged and distracting.

Is there a way to make the background extend the whole width of the tree element, so that the whole row gets colored? Either starting at the left border or starting at the beginning of the label, but definitely extending till the right border of the component?

Here is a small self-contained demo, based on this question.

import java.awt.*;
import javax.swing.*;
import javax.swing.tree.*;
public class SO26724913 {
    public static void main(String[] args) {
        DefaultMutableTreeNode a = new DefaultMutableTreeNode("a");
        DefaultMutableTreeNode b = new DefaultMutableTreeNode("b");
        DefaultMutableTreeNode c = new DefaultMutableTreeNode("c");
        a.add(b);
        a.add(c);
        final JTree tree = new JTree(a);
        tree.setCellRenderer(new DefaultTreeCellRenderer() {
                @Override
                public Component getTreeCellRendererComponent
                    (JTree tree, Object value, boolean selected,
                     boolean expanded, boolean leaf, int row, boolean focus)
                {
                    JComponent c = (JComponent)
                        super.getTreeCellRendererComponent
                        (tree, value, selected, expanded, leaf, row, focus);
                    DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
                    String data = (String)node.getUserObject();
                    if ("b".equals(data)) {
                        c.setBackground(Color.RED);
                        c.setOpaque(true);
                    }
                    else {
                        c.setBackground(null);
                        c.setOpaque(false);
                    }
                    return c;
                }
            });
        JFrame frm = new JFrame();
        frm.getContentPane().add(tree);
        frm.setSize(200, 200);
        frm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frm.setVisible(true);
    }
}

Current rendering This is what the code currently generates.

Background starting at icon I'd prefer either this
Background starting at left border or this.

like image 870
MvG Avatar asked Nov 03 '14 23:11

MvG


1 Answers

You might be able to override the paintComponent(Graphics) method of JTree to paint selection rectangles directly:

enter image description here

import java.awt.*;
import java.awt.event.*;
import java.util.Arrays;
import javax.swing.*;
import javax.swing.tree.*;

public class ColorTreeTest {
  private static final Color SELC = Color.RED;
  public JComponent makeUI() {
    FocusListener fl = new FocusListener() {
      @Override public void focusGained(FocusEvent e) {
        e.getComponent().repaint();
      }
      @Override public void focusLost(FocusEvent e) {
        e.getComponent().repaint();
      }
    };
    DefaultTreeCellRenderer r = new DefaultTreeCellRenderer() {
      @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, false);
        l.setBackground(selected ? Color.RED
                                 : tree.getBackground());
        l.setOpaque(true);
        return l;
      }
    };
    JPanel p = new JPanel(new GridLayout(1, 2));
    for (JTree t : Arrays.asList(new ColorTree1(), new ColorTree2())) {
      t.addFocusListener(fl);
      t.setCellRenderer(r);
      t.setOpaque(false);
      p.add(new JScrollPane(t));
    }
    return p;
  }
  static class ColorTree1 extends JTree {
    @Override public 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(r.x, r.y, getWidth() - r.x, r.height);
        }
      }
      super.paintComponent(g);
      if (getLeadSelectionPath() != null) {
        Rectangle r = getRowBounds(getRowForPath(getLeadSelectionPath()));
        g.setColor(hasFocus() ? SELC.darker() : SELC);
        g.drawRect(r.x, r.y, getWidth() - r.x - 1, r.height - 1);
      }
    }
  }
  static class ColorTree2 extends JTree {
    private static final Color SELC = Color.RED;
    @Override public void paintComponent(Graphics g) {
      g.setColor(getBackground());
      g.fillRect(0, 0, getWidth(), getHeight());
      if (getSelectionCount() > 0) {
        g.setColor(SELC);
        //@see http://ateraimemo.com/Swing/TreeRowSelection.html
        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);
      }
    }
  }
  public static void main(String... args) {
    EventQueue.invokeLater(new Runnable() {
      @Override public void run() {
        createAndShowGUI();
      }
    });
  }
  public static void createAndShowGUI() {
    JFrame f = new JFrame();
    f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    f.getContentPane().add(new ColorTreeTest().makeUI());
    f.setSize(320, 240);
    f.setLocationRelativeTo(null);
    f.setVisible(true);
  }
}
like image 71
aterai Avatar answered Oct 22 '22 08:10

aterai