如何正确地为JTree和所有节点着色

时间:2021-05-19 12:36:23

Let me explain what I need and how far I've come:

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

I have a JTree with a custom model and some nice icons and everything works as it should, however, the default colours of the tree (white) do not fit in with the GUI that I am trying to design and I'd like to be able to change its background colour.

我有一个带有自定义模型的JTree和一些漂亮的图标,一切都按原样运行,但是,树的默认颜色(白色)不适合我试图设计的GUI,我想成为能够改变它的背景颜色。

This has proven to be rather a chore!

这被证明是一件苦差事!

I have seen lots of explanations for this around the net, and they seem to revolve around either extending the DefaultTreeCellRenderer or the BasicTreeUI, but there is some suggestion that these are hacks and should be avoided, although there is little explanation of why they are hacks or what should be done instead.

我已经在网上看到了很多关于这个的解释,它们似乎围绕着扩展DefaultTreeCellRenderer或BasicTreeUI,但是有一些暗示这些是黑客并应该避免,尽管几乎没有解释为什么它们是黑客或者应该做些什么。

In any case none of the suggested methods seems to work very well because I always end up with one of two scenarios:

在任何情况下,没有一个建议的方法似乎工作得很好,因为我总是最终得到两个场景之一:

The colour is set in the tree but there are nasty gaps between the end of tree labels and the right side of the tree, and between the labels and their icons, and the plus/minus widgets for expanding the tree. In this case it is suggested that full row selection be implemented.

颜色在树中设置,但树标签的末尾和树的右侧之间,标签和它们的图标之间以及用于展开树的正/负小部件之间存在令人讨厌的间隙。在这种情况下,建议实施完整的行选择。

The other scenario has full row selection implemented and the nodes are correctly coloured and full row selection works, but the icons are either missing entirely, along with the plus/minus widgets, or there remains a nasty white border around both.

另一种情况是实现了完整的行选择,并且节点正确着色并且完整行选择有效,但是图标要么完全丢失,要么加/减小部件,或者两者都有一个令人讨厌的白色边框。

My own attempts at this have failed and I am currently left with the following code, which colours the tree and the nodes, but still leaves nasty white borders at the end of tree labels and between all icons.

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

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

My question(s):

1) Naturally I would appreciate some help getting a colour set on the JTree and I am open to suggestion, although if you plan to post a link there is a good chance I've already been there in the last few days, but also ...

1)当然,我很感激有一些帮助在JTree上获得颜色设置,我很乐意接受建议,尽管如果你打算发布一个链接,我很有可能在过去的几天里一直在那里,但也是。 ..

2) Id appreciate a definitive explanation of precisely what is involved in the process. Iv read through many tutorials concerning the JTree but none focus for long (if at all) on the question of completely colouring a tree, which is what I am looking for, and there seems to be some discussion about precisely what is required, for example full row selection or not, subclassing or not, etc.

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

I would also like to understand why certain things are considered 'hacks' and what the alternatives are.

我还想了解为什么某些东西被认为是“黑客”以及替代品是什么。


EDIT - A Solution

I am adding this section to show my progress in getting this sorted out and to perhaps give others a little head start on what has been a rather fiddly process. Iv spent days trying to get this to work properly with the System L&F and I have been in circles a few times. No doubt much of this is down to inexperience so if this saves someone else a few hair pulling sessions, all the better!

我正在添加这一部分,以显示我在整理过程中取得的进展,并可能让其他人在一个相当繁琐的过程中先行一点。我花了好几天试图让这个系统L&F正常工作,我已经圈了几次。毫无疑问,这很大程度上取决于缺乏经验,所以如果这能为其他人节省一些头发拉动会话,那就更好了!

BTW: I am more than happy to hear critique if I have done something that is regarded as a hack, but please provide a (working) alternative and some clear reasoning behind the label of Hack.

顺便说一句:如果我做了一些被视为黑客攻击的事情,我会非常高兴听到评论,但请提供一个(工作)替代方案,以及Hack标签背后的一些明确推理。

Okay, lets start: I want to have the system L&F set because I'm none too fond of the other options and because I like users to see a program that matches things that they are used to. I set the L&F like this before I do anything else:

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

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

This sets the the JTree to the standard for whatever OS you are using. This seems to include setting the background of the tree itself, as well as the background for node selections and so on. You are ready to go if all you want is the standard JTree, but while I want to see system based scrollbars etc, I also want to see colours other than white for the background.

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

I set the background to a nice blue colour like this (Note that the colour is used again in a moment, hence the final declaration):

我将背景设置为漂亮的蓝色(请注意,颜色会在一瞬间再次使用,因此最终声明):

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

What I have now is a JTree with a nice blue background which covers only those areas of the tree that do not contain nodes, which remain white according to the settings of the L&F. I now need to set some colour into the nodes themselves and to do this I need to override the getTreeCellRendererComponent of the DefaultTreeCellRenderer with the following code:

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

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

Here is the createImageIcon from above. This came direct from a tutorial, but note that this is edited before I'm finished with it (So don't copy and paste this code!):

这是上面的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;
    }
}

If you are following the code then at this stage you will see that I have set various colours for the tree nodes, as well as adding some icons to them. All this works very well, however there are now nasty white blocks between the ends of the node text and the edge of the JTree itself, and also between the node text and icon, and the plus/minus widgets of the tree. These white blocks also run through the tree as it expands.

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

This is pretty much where I came in and the hair pulling began. Thanks to Jacob' suggestion and MadProgrammer' tip (thanks guys) I was on the right track, but this is where things got strange, most likely because I had already been messing about with the JTree to try and get things working. Rolling the code back to a previous version and starting this section again is what helped to get things working.

这几乎是我进来的地方,头发拉开了。感谢雅各布的建议和MadProgrammer的提示(谢谢你们),我在正确的轨道上,但这是事情变得奇怪的地方,很可能是因为我一直在搞乱JTree试图让事情发挥作用。将代码回滚到以前的版本并再次启动此部分是有助于实现工作的原因。

Quick note: Please, if I'm doing this the wrong way, let me know!

快速注意:如果我这样做的方式不对,请告诉我!

It appeared that to change the white parts of the JTree I was going to need to override the BasicTreeUI, which I did like this:

似乎要更改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;
    }

});

I must state that this is not my code - I found this on one of the many tutorial sites and it worked as suggested and my JTree now had a correctly painted background, however, the plus/minus widgets had disapeared!

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

There is most likely an easy way to set these widgets from within the code above, however I was mindful of Jacob's warning about messing with the BasicTreeUI so I placed the following code directly after I set the L&F:

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

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)

This sets the plus/minus widget icons to some of my own and sets the type of painted lines I want between the elements in the tree. Note that at this point I got an error because the non-static createImageIcon from earlier could not be accessed. I had to edit the code like this to allow both calls to this method:

这会将加/减小部件图标设置为我自己的一些,并在树中的元素之间设置我想要的画线类型。请注意,此时我收到错误,因为无法访问前面的非静态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;
        }
}

I now (finally!) have a JTree, with the System L&F set, and with a colour set for all elements of the tree.

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

There are still unresolved issues:

仍有未解决的问题:

I do not (yet) fully understand the BasicTreeUI() code - this has given me the colours I require but also seems to have given me full row selection. However I have been unable to get the selection colour to span the whole distance. I intend to play with this later, but I will be cautious thanks to Jacob' warning - If anyone has any hard and fast rules concerning this I'm more than happy to hear them.

我(还)没有完全理解BasicTreeUI()代码 - 这给了我我需要的颜色,但似乎也给了我全行选择。但是我无法让选择颜色跨越整个距离。我打算稍后再玩这个,但是由于雅各布的警告,我会保持谨慎 - 如果有人对此有任何严格的规定,我会非常乐意听到他们。

I also deliberately set the L&F so that users would see something with which they were familiar, albeit with different colours. However, I have been forced to add my own icons to the plus/minus tree widgets, which is not really an issue, but rather a little niggle. This may well be cleared up at a later time.

我还故意设置L&F,以便用户可以看到他们熟悉的东西,尽管颜色不同。但是,我*将自己的图标添加到加/减树小部件,这不是一个真正的问题,而是一点点麻烦。这可能会在以后清理。

My other concern is that when setting values with UIManager I am only able to set certain keys, the others apparently being ignored. MadProgrammer did make the point that using Nimbus could mess these keys up a little, so perhaps using the system L&F has its own quirks.

另一个问题是,当使用UIManager设置值时,我只能设置某些键,其他键显然被忽略了。 MadProgrammer确实指出,使用Nimbus可能会使这些键混乱一点,所以也许使用L&F系统有它自己的怪癖。

Anyway, that is the current result of my efforts. I apologise for the length of this post, but I hope that this can help someone else and also that if I've done something in the wrong way, that someone can point out to me the correct way to go about it.

无论如何,这是我努力的当前结果。我为这篇文章的篇幅道歉,但我希望这可以帮助其他人,并且如果我以错误的方式做某事,那么有人可以向我指出正确的方法。

Regards

MVK

2 个解决方案

#1


3  

If you've already overridden getTreeCellRendererComponent so that the tree items have the color you want, you can use

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

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

to change the color of "unoccupied" space in the tree. Note that this will affect all future trees. The component UI classes use this property and many like it when drawing their components. To find out which keys exist you can do something like this:

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

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

As for your other questions, I'd never consider overriding getTreeCellRendererComponent to be a hack, but carelessly extending BasicTreeUI may cause unexpected behavior on some look and feels.

至于你的其他问题,我永远不会考虑重写getTreeCellRendererComponent是一个黑客,但不小心扩展BasicTreeUI可能会导致一些外观和感觉出现意外行为。

#2


0  

If all you're looking for is a way to have both System L&F and colored background + text in a JTree this is all the code you should need:

如果您正在寻找的是一种在JTree中同时具有系统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();
    }

#1


3  

If you've already overridden getTreeCellRendererComponent so that the tree items have the color you want, you can use

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

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

to change the color of "unoccupied" space in the tree. Note that this will affect all future trees. The component UI classes use this property and many like it when drawing their components. To find out which keys exist you can do something like this:

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

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

As for your other questions, I'd never consider overriding getTreeCellRendererComponent to be a hack, but carelessly extending BasicTreeUI may cause unexpected behavior on some look and feels.

至于你的其他问题,我永远不会考虑重写getTreeCellRendererComponent是一个黑客,但不小心扩展BasicTreeUI可能会导致一些外观和感觉出现意外行为。

#2


0  

If all you're looking for is a way to have both System L&F and colored background + text in a JTree this is all the code you should need:

如果您正在寻找的是一种在JTree中同时具有系统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();
    }