简体   繁体   English

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

[英]How to correctly colour a JTree and all nodes

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. 我已经在网上看到了很多这方面的解释,它们似乎围绕着扩展DefaultTreeCellRendererBasicTreeUI ,但有一些暗示这些都是黑客,应该避免,尽管几乎没有解释为什么它们是黑客或者应该做些什么。

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. 2)我非常欣赏对该过程所涉及的内容的明确解释。 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. 我阅读了很多关于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. 我花了好几天试图让这个系统L&F正常工作,我已经圈了几次。 No doubt much of this is down to inexperience so if this saves someone else a few hair pulling sessions, all the better! 毫无疑问,这很大程度上取决于缺乏经验,所以如果这能为其他人节省一些头发拉动会话,那就更好了!

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. 好吧,让我们开始:我想设置系统L&F,因为我不太喜欢其他选项,因为我喜欢用户看到一个程序,它匹配他们习惯的东西。 I set the L&F like this before I do anything else: 在我做任何其他事情之前,我将这样的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. 这将JTree设置为您正在使用的任何操作系统的标准。 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 ,你准备好了,但是当我想看到基于系统的滚动条等时,我也希望看到背景以外的颜色而不是白色。

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声明):

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. 我现在拥有的是具有漂亮蓝色背景的JTree ,它仅覆盖树中不包含节点的那些区域,根据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: 我现在需要为节点本身设置一些颜色,为此我需要使用以下代码覆盖DefaultTreeCellRenderergetTreeCellRendererComponent

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. 这是上面的createImageIcon 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!): 这直接来自一个教程,但请注意,这是在我完成之前编辑的(所以不要复制并粘贴代码!):

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. 所有这些都很有效,但是现在节点文本的末端和JTree本身的边缘之间以及节点文本和图标之间以及树的正/负小部件之间存在令人讨厌的白色块。 These white blocks also run through the tree as it expands. 这些白色块在扩展时也会穿过树。

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. 感谢雅各布的建议和MadProgrammer的提示(谢谢你们),我在正确的轨道上,但这是事情变得奇怪的地方,很可能是因为我一直在搞乱JTree试图让事情发挥作用。 Rolling the code back to a previous version and starting this section again is what helped to get things working. 将代码回滚到以前的版本并再次启动此部分有助于使事情正常运行。

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: 很可能是从上面的代码中设置这些小部件的简单方法,但是我注意到雅各布关于搞乱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. 请注意,此时我收到错误,因为无法访问前面的非静态createImageIcon I had to edit the code like this to allow both calls to this method: 我必须编辑这样的代码,以允许同时调用此方法:

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. 我(还)没有完全理解BasicTreeUI()代码 - 这给了我我需要的颜色,但似乎也给了我全行选择。 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. 我打算稍后再玩这个,但是由于雅各布的警告,我会保持谨慎 - 如果有人对此有任何严格的规定,我很高兴听到他们。

I also deliberately set the L&F so that users would see something with which they were familiar, albeit with different colours. 我还故意设置了L&F,以便用户可以看到他们熟悉的东西,尽管颜色不同。 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. 这可能会在以后清理。

My other concern is that when setting values with UIManager I am only able to set certain keys, the others apparently being ignored. 我的另一个问题是,当使用UIManager设置值时,我只能设置某些键,其他键显然被忽略了。 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. 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 MVK

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. 组件UI类使用此属性,许多人在绘制组件时都喜欢它。 To find out which keys exist you can do something like this: 要找出存在哪些键,您可以执行以下操作:

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是一个hack,但不小心扩展BasicTreeUI可能会导致一些外观和感觉出现意外行为。

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

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

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