1

伙计们,

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

4 回答 4

5

这是一个真正的痛苦。DefaultTreeCellRenderer无论如何都会忽略该值opaque并填充其内容。但是,您可以尝试一个标志。我以前做过,但没有时间测试它......

试试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));
        }
    }
}
于 2012-12-06T02:30:37.177 回答
2

回答

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

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

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;

最终结果是(假设我们通过反射强制访问该字段,fi),无论 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());
}

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

于 2012-12-06T14:45:05.607 回答
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); 似乎解决了它。

于 2015-08-11T01:03:32.610 回答
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

于 2015-11-05T21:47:55.890 回答