繁体   English   中英

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

[英]How to set transparent background for JTree cell?

民间,

我正在尝试创建一个渐变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;
        }
    }
}

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

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

更新

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

没有| 用...

在此处输入图片说明在此处输入图片说明

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

回答

(扩展@Mad的答案,对基本问题的冗长分析在最后):

如果要使global属性在手动设置为树的defaultTreeCellRenderer中生效,则该渲染器必须再次调用updateUI,

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

如果你不想改变全局设置,并有透明渲染只有一些树的实例-该选项

  • 要么从头开始实现TreeCellRenderer,然后消除所有脏污(例如覆盖油漆并执行一些意外的硬编码技巧...哎呀!)
  • 通过在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;

最终结果是(假设我们通过反射强制访问该字段),无论UIManager中的设置如何,始终执行以下操作。

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

与此不同寻常的是:为什么让UI安装默认设置时,UIManager 的设置会生效? 这是因为渲染器的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());
}

顺便说一句:这种实例化混乱似乎是在jdk7中引入的...很可能(虽然没有检查)渲染器颜色的默认设置也不起作用。

如何像这样扩展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); 似乎解决了。

在这些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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM