改变Nimbus LaF如何处理JTree节点高亮显示

8
我一直在努力将Java应用程序从WindowsLookAndFeel转换为Nimbus,尽管Nimbus有些小问题,但我已经取得了很大的成功。总体而言,我的用户喜欢Nimbus LaF,但是他们不喜欢一些细节,其中一些我通过参考此网站上的以前的问题来进行更改。例如:我从Windows LaF中复制了LeafIcon、ClosedIcon和OpenIcon(他们喜欢),并将它们用于Nimbus版本,从而实现了两种LaFs的完美结合。
但最后遇到了一个难题:
我有一个JTree,使用子类化的DefaultCellRenderer创建适当的节点显示,对于WindowsLookAndFeel这很好用。
问题在于: 在WindowsLaF下,选择一个节点时,节点的文本会被突出显示,视觉效果很容易理解。但在Nimbus下,选择一个节点时,突出显示是通过一条(相当暗的)颜色栏完成的,该颜色栏跨越整个树窗口的宽度(而不仅仅是文本的宽度),这种效果让人困惑。
因此,我只想在Nimbus LaF中使用WindowsLaF的JTree节点突出显示效果(即仅在文本宽度内具有彩色背景,并且最好是我可以选择的更好颜色)。我认为这意味着我需要将正确的Painter分配给“Tree: TreeCell [Focused + Selected] .backgroundPainter”,但我不知道该如何编写它。
欢迎提供建议。
public class TreeBorder {
    public static void main( String[] args ) {
        try{
            for( UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels() ) {
                if( "Nimbus".equals( info.getName() ) ) {
                    UIManager.setLookAndFeel( info.getClassName() );
                    break;
                }
            }
        } catch( Exception e ) {
            e.printStackTrace();
        }
        SwingUtilities.invokeLater( new Runnable() {
            @Override
            public void run() {
                JFrame f = new JFrame();
                f.setLocationRelativeTo( null );
                f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
                f.getContentPane().add( getJTree() );
                f.pack();
                f.setVisible( true );
            }
            private JTree getJTree() {
                JTree jTree = new JTree();
                jTree.setCellRenderer( new LocalRenderer() );
                return jTree;
            }
        } );
    }

    private static class LocalRenderer extends DefaultTreeCellRenderer {
        @Override
        public Component getTreeCellRendererComponent( JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasfocus ) {
            DefaultTreeCellRenderer result = (DefaultTreeCellRenderer)super.getTreeCellRendererComponent( tree, value, sel, expanded, leaf, row, hasfocus );
                if( true ) {
                    result.setFont( new JLabel().getFont() );
                    Icon icon = UIManager.getIcon("FileView.floppyDriveIcon");
                    result.setIcon( icon );
                }
            return(result);
        }
    }
}

我的补充说明:当然,如果有比使用绘图工具更简单的方法,那就更好了。尝试将Tree.selectionBackground更改为不那么分散注意力的颜色,但Nimbus似乎忽略了这个修改。 - user1359010
关于我的评论,你不能直接设置Tree.selectionBackground,但是这个颜色是从numbusSelectionBackground派生出来的,所以nimbUID.put("nimbusSelectionBackground", new ColorUIResource(205,208,216));可以产生一些期望的效果(更浅的Tree.selectionBackground)。仍然需要帮助来实现只高亮文本而不是整行的效果。 - user1359010
@oliholz,请您能否评论一下您的悬赏问题?我从未见过这个问题,因为经典渲染器的概念会在大多数情况下重载Nimbus属性和设置。 - mKorbel
@mKorbel eee 编辑。使用DefaultTreeCellRenderer和Java7,我得到了在Nimbus下烦人的高亮显示。 - oliholz
Nimbus bug在Java 8中已修复,而此修复方法现已失效。 - oliholz
1个回答

6
编辑
“Tree.selectionBackground”键控制JTree上的高亮显示 - 它在树级别上完成,而不是在TreeCellRenderer级别上完成(这就是为什么它有点难以管理的原因)。此代码将为您提供可以控制突出显示的树:
private JTree getJTree() {

    JTree jTree = new JTree();
    jTree.setOpaque(true);
    jTree.setBackground(Color.white);
    UIDefaults paneDefaults = new UIDefaults();
    paneDefaults.put("Tree.selectionBackground",null);

    JTextPane pane = new JTextPane();
    jTree.putClientProperty("Nimbus.Overrides",paneDefaults);
    jTree.putClientProperty("Nimbus.Overrides.InheritDefaults",false);

    jTree.setCellRenderer( new LocalRenderer() );
    return jTree;
}

这是将高亮颜色更改为红色的示例。请注意,图标的背景也会被突出显示 - 这也是非 Nimbus L&F 的默认行为。如果您不希望图标被突出显示,您需要使用比默认 JLabel 更复杂的渲染 TreeCell 的方法:
    public Component getTreeCellRendererComponent( JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasfocus ) {
        DefaultTreeCellRenderer result = (DefaultTreeCellRenderer)super.getTreeCellRendererComponent( tree, value, sel, expanded, leaf, row, hasfocus );
        result.setOpaque(true);
            if( true ) {
                result.setFont( new JLabel().getFont() );
                Icon icon = UIManager.getIcon("FileView.floppyDriveIcon");
                result.setIcon( icon );
            }
            if(sel){
                result.setBackground(Color.red);
            } else{
                result.setBackground(Color.white);
            }
        return(result);
    }

“原始答案:
解决这个问题的最简单方法之一是将所选背景颜色设置为透明。问题在于它试图绘制标签的背景 - 这个标签没有JTree选择使用的酷Nimbus painter。因此,在getTreeCellRendererComponent方法中添加以下行:”
result.setBackgroundSelectionColor(new Color(0,0,0,0));

另一个选择是使用nimbus painter作为TreeCellRenderer的背景,但在这种情况下似乎有些大材小用。

嗯...不是很确定,但我理解这个问题是关于防止渲染器外部的背景着色(同时保持其内部)。使用透明颜色(如果有任何效果)将会同时防止两者。 - kleopatra
@kleopatra 呵呵...也许下次我会仔细阅读整个问题。干得好。 - Nick Rippe
楼主在这里,很久没有回来了,很高兴看到问题得到了解答,并且代码可以正常运行。非常感谢提供代码以及关于它在树层面上的处理信息。 - user1359010
不再与Java 8兼容。但Nimbus错误已修复。 - oliholz
楼主再次出现。@oliholz:很抱歉,但我没有看到修复Nimbus错误。尝试构建一个简单的树;在默认L&F和Numbus中选择时,高亮行为会有所不同。默认情况下,只突出显示节点文本,而Nimbus则突出显示树框架的宽度(这是我的原始问题)。 借鉴了http://stackoverflow.com/questions/28691272/jtree-ignoring-laf-overrides上最后一个答案中的想法,并使用了一种只是轻微减淡颜色的绘画工具,效果更好。仍然希望将突出显示限制为节点文本。欢迎提供建议。 - user1359010
在我的上面的评论中补充一点。有趣的(?)讽刺:https://dev59.com/j4Tba4cB1Zd3GeqP_9q2 本质上是问相反的问题——如何在默认的 L&F 中获得完整的颜色带而不仅仅是突出显示节点文本。 - user1359010

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接