JTree中的颜色行

Posted

技术标签:

【中文标题】JTree中的颜色行【英文标题】:Color row in JTree 【发布时间】:2014-12-30 17:44:24 【问题描述】:

我想为 JTree 中的元素着色。然而,仅仅为标签添加背景颜色看起来有点奇怪。特别是如果选择了多个节点,生成的形状看起来参差不齐且分散注意力。

有没有办法让背景延伸到树元素的整个宽度,让整行都被着色?要么从左边框开始,要么从标签的开头开始,但肯定一直延伸到组件的右边框?

这是一个基于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);
    

这是代码当前生成的内容。

我更喜欢这个 或这个。

【问题讨论】:

如果为 super.getTreeCellRendererComponent() 返回的组件调用 setPreferredSize() 使其宽度大到足以覆盖整行怎么办? @StanislavL:这在几个地方破坏了自动大小计算。覆盖getPreferredSize 而是确保照常计算高度。但是,计算整棵树的宽度将变得不可能。不过,有趣的方法可能值得回答,因此用户可以对此进行投票。 绝对覆盖 getPreferredSize() 更好地保持高度。仅当宽度小于 JTree 宽度以保持默认 JTree 宽度计算不变时才增加宽度也是一种明智的方法。 @StanislavL:由于某种原因,tree.getWidth() 在我的单元格渲染器中为零。似乎单元格被渲染为一些缓冲图像,然后计算树大小,并且单元格不再被渲染。 【参考方案1】:

您也许可以覆盖JTreepaintComponent(Graphics) 方法来直接绘制选择矩形:

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);
  

【讨论】:

我想为了性能起见,我会尝试考虑图形上下文的剪辑边界,但这只是细节。除此之外,这对我来说效果很好。谢谢! 我有一些问题,但是使用 JXtreeTable,可以在我的对象上应用这个解决方案吗? 某些系统外观(Linux、CentOS 7、Java 1.8)会在调用super.paintComponent(g); 时重新绘制整个行的背景,从而使该解决方案部分失效。您通常可以通过默认情况下呈现单元格选择的方式来发现此类 L&F 实现 - 如果选择突出显示矩形跨越整行,而不仅仅是渲染器的单元格(第三个与问题图像的第一个示例),您可能是 out of luck。跨度>

以上是关于JTree中的颜色行的主要内容,如果未能解决你的问题,请参考以下文章

Java Swing:需要具有复选框的高质量开发 JTree

动态更改 NatTable 中的行颜色

如何根据严重性更改数据网格中的行颜色?

BIRT:表组中的交替行颜色

有没有办法使用 xml 中的“颜色”标签来更改单独的 <td> 行字体颜色

什么决定了 jquery 数据表样式中的行颜色?