4

让我解释一下我需要什么以及我已经走了多远:

我有JTree一个自定义模型和一些漂亮的图标,一切正常,但是,树的默认颜色(白色)不适合我正在尝试设计的 GUI,我希望能够更改其背景颜色。

事实证明,这很麻烦!

我在网上看到了很多对此的解释,它们似乎围绕着扩展 theDefaultTreeCellRenderer应该改为。BasicTreeUI

无论如何,建议的方法似乎都不是很好,因为我总是遇到以下两种情况之一:

颜色是在树中设置的,但是在树标签的末端和树的右侧之间、标签和它们的图标之间以及用于扩展树的加/减小部件之间存在令人讨厌的间隙。在这种情况下,建议实施全行选择。

另一种情况是实现了全行选择,节点颜色正确,全行选择有效,但图标或者完全丢失,连同加/减小部件,或者两者周围仍然存在令人讨厌的白色边框。

我自己的尝试失败了,我目前只剩下以下代码,它为树和节点着色,但仍然在树标签的末尾和所有图标之间留下令人讨厌的白色边框。

final Color MainBg = new Color(213,220,228);
KTree.setCellRenderer(new DefaultTreeCellRenderer()
{
    @Override
    public Component getTreeCellRendererComponent(JTree pTree, Object pValue, boolean pIsSelected, boolean pIsExpanded, boolean pIsLeaf, int pRow, boolean pHasFocus)
    {
            super.getTreeCellRendererComponent(pTree, pValue, pIsSelected, pIsExpanded, pIsLeaf, pRow, pHasFocus);
            setBackgroundNonSelectionColor(MainBg);
            setBackgroundSelectionColor(MainBg);
            setTextNonSelectionColor(Color.BLACK);
            setTextSelectionColor(Color.BLACK);
            ImageIcon tDoc = createImageIcon("images"  + File.separator + "document.gif","document");
            ImageIcon tOpen = createImageIcon("images"  + File.separator + "book_open.gif","book open");
            ImageIcon tClosed = createImageIcon("images"  + File.separator + "bookclosed.png","book closed");
            setClosedIcon(tClosed);
            setOpenIcon(tOpen);
            setLeafIcon(tDoc);
            putClientProperty("Tree.collapsedIcon", tDoc);
            putClientProperty("tree.expandedIcon", tOpen);
        return (this);
    }
});

我的问题:

1)当然,我会很感激一些帮助JTree,我愿意接受建议,虽然如果你打算发布一个链接,我很可能在过去几天已经去过那里,而且.. .

2)我很欣赏对这个过程所涉及的确切内容的明确解释。我通读了许多关于JTree但没有长期关注的教程(如果有的话)完全着色一棵树的问题,这是我正在寻找的,并且似乎有一些关于确切需要什么的讨论,例如完整行选择与否,子类化与否等。

我还想了解为什么某些事情被认为是“黑客”以及替代方案是什么。


编辑 - 一个解决方案

我添加这一部分是为了展示我在解决这个问题方面取得的进展,并可能让其他人在这个相当繁琐的过程中领先一步。我花了几天的时间试图让它与 System L&F 一起正常工作,我已经转了好几次了。毫无疑问,这很大程度上是由于缺乏经验,所以如果这可以为​​其他人节省一些拉头发的时间,那就更好了!

顺便说一句:如果我做了一些被认为是 hack 的事情,我很乐意听到批评,但请提供一个(可行的)替代方案以及 Hack 标签背后的一些明确推理。

好的,让我们开始吧:我想要设置系统 L&F,因为我不太喜欢其他选项,而且我喜欢用户看到与他们习惯的事物相匹配的程序。在我做任何其他事情之前,我这样设置 L&F:

try
    {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
    }
catch (Exception e) { return; }

JTree将为您使用的任何操作系统设置标准。这似乎包括设置树本身的背景,以及节点选择的背景等等。如果你想要的只是标准JTree,你就可以开始了,但是当我想看到基于系统的滚动条等时,我也想看到除白色以外的颜色作为背景。

我将背景设置为这样的漂亮蓝色(请注意,稍后会再次使用该颜色,因此final声明):

final Color MainBg = new Color(213,220,228);
    KTree.setBackground(MainBg);

我现在拥有的是JTree一个漂亮的蓝色背景,它仅覆盖树中不包含节点的区域,根据 L&F 的设置,这些区域保持白色。我现在需要为节点本身设置一些颜色,为此我需要使用以下代码覆盖getTreeCellRendererComponentof :DefaultTreeCellRenderer

final Color SellBg = new Color(232,235,237);
final Color HiliBg = new Color(150,196,246);

KTree.setCellRenderer(new DefaultTreeCellRenderer()
{

    @Override
    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);
        setBackgroundNonSelectionColor(MainBg);
        setBackgroundSelectionColor(SellBg);
        setTextNonSelectionColor(Color.BLACK);
        setTextSelectionColor(Color.BLACK);
        ImageIcon tDoc = createImageIcon("images"  + File.separator + "document.gif","document");
        ImageIcon tOpen = createImageIcon("images"  + File.separator + "book_open.gif","open");
        ImageIcon tClosed = createImageIcon("images"  + File.separator + "bookclosed.png","closed");
        setClosedIcon(tClosed);
        setOpenIcon(tOpen);
        setLeafIcon(tDoc);
        setBorderSelectionColor(HiliBg);
        return this;
    }
});

这是上面的createImageIcon。这直接来自教程,但请注意,这是在我完成之前编辑的(所以不要复制和粘贴代码!):

protected ImageIcon createImageIcon(String path, String description)
{
    java.net.URL imgURL = getClass().getResource(path);
    if (imgURL != null)
    {
        return new ImageIcon(imgURL, description);
    }
    else
    {
        System.err.println("Couldn't find file: " + path);
        return null;
    }
}

如果您遵循代码,那么在此阶段您将看到我为树节点设置了各种颜色,并为它们添加了一些图标。所有这一切都很好,但是现在在节点文本的末端和JTree自身的边缘之间,以及节点文本和图标之间,以及树的加/减小部件之间都有令人讨厌的白色块。随着树的扩展,这些白色块也会穿过树。

这几乎就是我进来的地方,开始拉头发。多亏了 Jacob 的建议和 MadProgrammer 的提示(谢谢大家),我走在了正确的轨道上,但这就是事情变得奇怪的地方,很可能是因为我已经在搞砸JTree试图让事情正常进行。将代码回滚到以前的版本并重新开始本节有助于使事情正常进行。

快速说明:如果我做错了,请告诉我!

看来要更改JTree我需要覆盖的白色部分BasicTreeUI,我这样做是这样的:

KTree.setUI(new javax.swing.plaf.basic.BasicTreeUI()
{
    @Override
    public Rectangle getPathBounds(JTree tree, TreePath path)
    {
        if(tree != null && treeState != null)
        {
            return getPathBounds(path, tree.getInsets(), new Rectangle());
        }
        return null;
    }
    private Rectangle getPathBounds(TreePath path, Insets insets, Rectangle bounds)
    {
        bounds = treeState.getBounds(path, bounds);
        if(bounds != null)
        {
            bounds.width = tree.getWidth();
            bounds.y += insets.top;
        }
        return bounds;
    }

});

我必须声明这不是我的代码 - 我在许多教程网站之一上找到了它,它按照建议工作,我JTree现在有一个正确绘制的背景,但是,加号/减号小部件已经消失了!

很可能有一种简单的方法可以从上面的代码中设置这些小部件,但是我注意到 Jacob 关于弄乱的警告,所以我在设置 L&F 后直接BasicTreeUI放置了以下代码:

ImageIcon clapsed = createImageIcon("images"  + File.separator + "plus.gif","closed");
ImageIcon clopen = createImageIcon("images"  + File.separator + "minus.gif","open");
UIManager.getLookAndFeelDefaults().put("Tree.collapsedIcon",clapsed);
UIManager.getLookAndFeelDefaults().put("Tree.expandedIcon",clopen);
UIManager.getLookAndFeelDefaults().put("Tree.paintLines", true);
UIManager.getLookAndFeelDefaults().put("Tree.leftChildIndent",7);
UIManager.getLookAndFeelDefaults().put("Tree.lineTypeDashed",true)

这会将加/减小部件图标设置为我自己的一些图标,并在树中的元素之间设置我想要的绘制线条的类型。请注意,此时我收到一个错误,因为无法访问之前的非静态createImageIcon 。我必须像这样编辑代码以允许调用此方法:

protected static ImageIcon createImageIcon(String path, String description)
{
    Class<?> cl=new Object(){}.getClass().getEnclosingClass();
        java.net.URL imgURL = cl.getResource(path);
        if (imgURL != null)
        {
            return new ImageIcon(imgURL, description);
        }
        else
        {
            System.err.println("Couldn't find file: " + path);
            return null;
        }
}

我现在(终于!)有一个JTree, 设置了系统 L&F,并为树的所有元素设置了颜色。

仍然存在未解决的问题:

我(尚未)完全理解BasicTreeUI()代码 - 这给了我所需的颜色,但似乎也给了我完整的行选择。但是我一直无法让选择颜色跨越整个距离。我打算稍后再玩,但感​​谢 Jacob 的警告,我会保持谨慎 - 如果有人对此有任何硬性规定,我很乐意听到。

我还特意设置了 L&F,以便用户可以看到他们熟悉的东西,尽管颜色不同。但是,我被迫将自己的图标添加到加/减树小部件中,这并不是真正的问题,而是有点小问题。这很可能在以后得到澄清。

我的另一个担忧是,当使用我设置值时,UIManager我只能设置某些键,而其他键显然被忽略了。MadProgrammer 确实指出使用 Nimbus 可能会使这些键有些混乱,因此使用 L&F 系统可能有其自身的怪癖。

不管怎样,这是我目前努力的结果。我为这篇文章的长度道歉,但我希望这可以帮助其他人,并且如果我以错误的方式做某事,有人可以向我指出正确的解决方法。

问候

MVK

4

2 回答 2

3

如果您已经覆盖getTreeCellRendererComponent,以便树项目具有您想要的颜色,您可以使用

UIManager.getLookAndFeelDefaults().put("Tree.background", new ColorUIResource(aColor);

更改树中“未占用”空间的颜色。请注意,这将影响所有未来的树。组件 UI 类在绘制其组件时使用此属性,许多人喜欢它。要找出存在哪些键,您可以执行以下操作:

for (Entry<Object, Object> entry : UIManager.getLookAndFeelDefaults().entrySet()) {
    System.out.println(entry.getKey() + " : " + entry.getValue());
}

至于您的其他问题,我永远不会认为覆盖getTreeCellRendererComponent是一种黑客行为,但粗心的扩展BasicTreeUI可能会导致某些外观和感觉出现意外行为。

于 2012-07-14T12:33:28.213 回答
0

如果您正在寻找的只是一种在 JTree 中同时拥有 System L&F 和彩色背景 + 文本的方法,那么这就是您应该需要的所有代码:

        try {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        UIManager.getLookAndFeelDefaults().put("Tree.background", new ColorUIResource(Util.BACKGROUND));
        UIManager.getLookAndFeelDefaults().put("Tree.textBackground", new ColorUIResource(Util.BACKGROUND));
    } catch (ClassNotFoundException | InstantiationException
            | IllegalAccessException | UnsupportedLookAndFeelException e) {
        e.printStackTrace();
    }
于 2017-04-26T19:26:44.690 回答