如何为JTree单元设置透明背景?

时间:2012-12-06 02:25:36

标签: java swing transparency jtree renderer

民间,

我正在尝试创建渐变JTree控件。除了树单元格的背景不透明外,以下代码大部分都有效。如果有人打电话告诉我,我做得不对,我将不胜感激。

提前感谢您的帮助。

问候,
彼得


package TestPackage;

import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import java.awt.*;

public class Test {

    public Test() {
        JFrame frame = new JFrame();

        JPanel framePanel = new JPanel();
        framePanel.setLayout(new BorderLayout());
        frame.setContentPane(framePanel);


        DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode("Item");
        DefaultMutableTreeNode childNode = new DefaultMutableTreeNode("Child");
        rootNode.add(childNode);

        GradientTree tree = new GradientTree(rootNode);
        // JTree tree = new JTree(rootNode);
        // tree.setBackground(Color.blue);
        tree.setCellRenderer(new MyRenderer());

        JScrollPane scroll = new JScrollPane(tree);
        scroll.setOpaque(false);
        framePanel.add(scroll, BorderLayout.CENTER);

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

    }

    public static void main(String[] args) {

        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new Test();
            }
        });
    }


    @SuppressWarnings("serial")
    public static class GradientTree extends JTree {

        public GradientTree(DefaultMutableTreeNode node) {
            super(node);
        }


        @Override
        protected void paintComponent(Graphics g) {

            int h = getHeight();
            int w = getWidth();

            GradientPaint gradientPaint = new GradientPaint(0, 0, Color.LIGHT_GRAY, 0, h, Color.WHITE);

            Graphics2D g2D = (Graphics2D) g;
            g2D.setPaint(gradientPaint);
            g2D.fillRect(0, 0, w, h);

            this.setOpaque(false);
            super.paintComponent(g);
            this.setOpaque(true);
        }
    }

    @SuppressWarnings({"serial" })
    private class MyRenderer extends DefaultTreeCellRenderer {
        public MyRenderer() {
            this.setOpaque(false);
            this.setForeground(Color.RED);
        }

        public Component getTreeCellRendererComponent(
                JTree tree,
                Object value,
                boolean sel,
                boolean expanded,
                boolean leaf,
                int row,
                boolean hasFocus) {

            super.getTreeCellRendererComponent(
                    tree, value, sel,
                    expanded, leaf, row,
                    hasFocus);

            return this;
        }
    }
}

4 个答案:

答案 0 :(得分:5)

这是一个真正的痛苦。 DefaultTreeCellRenderer将忽略opaque值并填写其内容。但是,你可以尝试一个标志。我过去做过,但没有时间测试它......

试试UIManager.put("Tree.rendererFillBackground", false)。尝试在任何渲染器之前执行此操作,但在应用任何外观设置之后。

<强>已更新

在创建任何树之前设置此属性非常重要

没有|与...

enter image description here enter image description here

public class TestTreeRenderer {

    public static void main(String[] args) {
        new TestTreeRenderer();
    }

    public TestTreeRenderer() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Test");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TreePane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }

        });
    }

    public class TreePane extends JPanel {

        private JTree tree;

        public TreePane() {
            // THIS IS VERY IMPORTANT
            // You must set this BEFORE creating ANY trees!!
            UIManager.put("Tree.rendererFillBackground", false);

            setLayout(new BorderLayout());
            tree = new JTree();
            tree.setBackground(Color.BLUE);

            System.out.println("Loading files...");
            File root = new File("/etc");
            DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(root.getName());
            for (File file : root.listFiles()) {
                rootNode.add(new DefaultMutableTreeNode(file.getName()));
            }
            System.out.println("Loading model");
            DefaultTreeModel model = new DefaultTreeModel(rootNode);
            tree.setModel(model);

            add(new JScrollPane(tree));
        }
    }
}

答案 1 :(得分:2)

回答

(扩展@ Mad的回答,对基本问题的长期分析最终):

如果希望全局属性在手动设置到树的defaultTreeCellRenderer中有效,则该渲染器必须再次调用updateUI ,f.i。

UIManager.put("Tree.rendererFillBackground", false);
    ...
TreeCellRenderer r = new DefaultTreeCellRenderer() {
     {
          updateUI();
     }
};
tree.setCellRenderer(r);

如果您想要更改全局设置并且透明渲染器只有一些树实例 - 选项是

  • 从头开始实现一个TreeCellRenderer并省去所有的肮脏(如重写油漆和做一些意想不到的硬编码技巧...... doooh!)
  • 通过在updateUI
  • 中临时设置ui属性来欺骗渲染器

欺骗代码:

TreeCellRenderer r = new DefaultTreeCellRenderer() {
    {
         updateUI();
    }

    @Override
    public void updateUI() {
        Object old = UIManager.get("Tree.rendererFillBackground");
        try {
            UIManager.put("Tree.rendererFillBackground", false);
            super.updateUI();
        } finally {
            UIManager.put("Tree.rendererFillBackground", old);
        }
    }
};

分析

从我的评论开始:

  

奇怪的是,仅仅设置CellRenderer(相对于让ui安装它的优点)会使旗帜无效

解决了这个难题:

DefaultTreeCellRenderer具有意图以从UIManager中的设置设置其fillBackground字段 - 但在实例化时失败。原因是 - 一个太常见的错误;-) - 实际上是在super的实例化中这样做,因为在super的构造函数中调用了一个重写的方法:

// this is implemented in DefaultTreeCellRenderer
// but called in JLabel constructor 
public void updateUI() {
    ....
    // we are in JLabel, that is fillBackground not yet known 
    fillBackground = DefaultLookup.getBoolean(this, ui, "Tree.rendererFillBackground", true);
    ...
}

然后在实例化过程中,字段值被硬编码:

private boolean fillBackground = true;

最终结果是(假设我们强制访问该字段,通过反射访问f.i.),无论UIManager中的设置如何,都会始终进行以下传递。

DefaultTreeCellRenderer renderer = new DefaultTreeRenderer();
assertTrue(renderer.fillBackground);

有了这个不寻常的东西:为什么 UIManager中的设置在让ui安装默认设置时有效?这里的原因是渲染器updateUI被调用两次:一次在实例化中,一次在树的updateUI中:

public void updateUI() {
    setUI((TreeUI)UIManager.getUI(this));
    // JW: at this point the renderer has its fillbackground hard-coded to true
    SwingUtilities.updateRendererOrEditorUI(getCellRenderer());
    // JW: now it's updateUI has been called again, and correctly set to the 
    // UIManager's value 
    SwingUtilities.updateRendererOrEditorUI(getCellEditor());
}
BTW:这个实例化混乱似乎是在jdk7中引入的......很可能(尽管没有检查)渲染器颜色的默认设置也不起作用。

答案 2 :(得分:0)

如何扩展DefaultTreeCellRenderer:

public class MyRenderer extends DefaultTreeCellRenderer {

public Component getTreeCellRendererComponent(JTree tree, Object value,
        boolean isSelected, boolean expanded, boolean leaf, int row,
        boolean hasFocus) {

    JComponent c = (JComponent) super.getTreeCellRendererComponent(tree, value, isSelected, expanded, leaf, row, hasFocus);

          c.setOpaque(true);

      return c; 
    }

}

设置c.setOpaque(true);似乎解决了它。

答案 3 :(得分:0)

我真的很犹豫在这些Swing专家面前提出这个假设......但是最近的JDK能否真正纠正这个问题呢?

我的应用程序中有这样的代码,似乎工作得很好...... JTree的背景完美无瑕...... NB Jython,但应该是可以理解的:

        def getTreeCellRendererComponent( self, tree, value, selected, expanded, leaf, row, has_focus ):
            super_comp = self.super__getTreeCellRendererComponent( tree, value, selected, expanded, leaf, row, has_focus )
            super_comp.opaque = not selected

            ...

Java版本是1.7.0_079