【问题标题】:Swing and Nimbus: Replace background of JPopupMenu (attached to JMenu)Swing 和 Nimbus:替换 JPopupMenu 的背景(附在 JMenu 上)
【发布时间】:2012-07-22 06:08:32
【问题描述】:

Nimbus 通常看起来不错,但对于某些颜色组合,效果并不理想。在我的情况下,JPopupMenu 的背景不适合,这就是我想手动设置它的原因。

我使用的是 Java 7,有趣的是,Nimbus 完全忽略了 UIManager 中某些属性的设置(例如 PopupMenu.background)。所以我唯一的选择是创建一个覆盖paintComponent(...)JPopupMenu 的子类。我知道,这很糟糕,但至少它奏效了。

但是,如果您将 JMenu 添加到另一个菜单,它会嵌入它自己的 JPopupMenu 实例,我无法弄清楚如何用我自己的子类替换它。

即使将自己的PopupMenuUI 分配给嵌入式实例也没有带来任何结果。如果直接从JPopupMenu 继承,则调用覆盖的paint(...) 方法,但是,无论我做什么,都没有绘制任何内容。如果从javax.swing.plaf.synth.SynthPopupMenuUI 继承paint 甚至没有被调用,结果是如果我根本没有设置自己的PopupMenuUI

所以简单的问题是:如何使用 Nimbus 作为 L&F 在 Java 7 上调整一个 JPopupMenu 或(如果更容易的话)所有这些的背景颜色?

编辑:代码示例

看看下面的代码和结果:

public static void main(final String[] args) {
    try {
        UIManager.setLookAndFeel(NimbusLookAndFeel.class.getCanonicalName());
        UIManager.getLookAndFeelDefaults().put("PopupMenu.background", Color.GREEN);
        UIManager.getLookAndFeelDefaults().put("Panel.background", Color.RED);
        UIManager.getLookAndFeelDefaults().put("List.background", Color.BLUE);
    } catch (ClassNotFoundException | InstantiationException
            | IllegalAccessException | UnsupportedLookAndFeelException e) {
        e.printStackTrace();
    }
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(200,200);

    JPanel panel = new JPanel(new BorderLayout());
    panel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
    JList list = new JList();
    panel.add(list);

    frame.getContentPane().add(panel);

    JPopupMenu menu = new JPopupMenu();
    menu.add(new JMenuItem("A"));
    menu.add(new JMenuItem("B"));
    menu.add(new JMenuItem("C"));

    frame.setVisible(true);
    menu.show(frame, 50, 50);
}

我知道,有人说你应该在设置 L&F 之前使用UIManager.put(key, value)UIManager.getLookAndFeelDefautls().put(key,value) ,但对我来说这并没有带来任何结果(意思是:没有更改默认颜色全部)。上面的代码至少带来了:

如果您使用JPopupMenu.setBackground(...),会发生同样的事情(没有任何意义)。这是因为 Nimbus 使用了内部绘制程序,它根据 Nimbus 的原色计算颜色并忽略组件的属性。在此示例中,您可以使用以下解决方法:

JPopupMenu menu = new JPopupMenu() {
    @Override
    public void paintComponent(final Graphics g) {
        g.setColor(Color.GREEN);
        g.fillRect(0,0,getWidth(), getHeight());
    }
};

带来了

但是,如果您插入一个 JMenu,它本身包装了一个您无法覆盖的 JPopupMenu,则此解决方法不起作用:

JMenu jmenu = new JMenu("D");
jmenu.add(new JMenuItem("E"));
menu.add(jmenu);

如预期的那样:

您可以使用JMenu.getPopupMenu() 检索此JPopupMenu,但您无法设置它。即使在 JMenu 自己的子类中重写此方法也不会带来任何结果,因为 JMenu 似乎在不使用 getter 的情况下访问它的封装实例 JPopupMenu

【问题讨论】:

  • 如果您概述您的实际目标,您可能会得到更好的答案。 UI 委托PopupMenuUI 并不重要。
  • @aRestless 我认为最好发布一个 SSCCE,Nimbus 不会忽略,它的开发在中间某个地方结束,当然还有(需要)添加一些代码行,另一个选项 could be or another combinations
  • @aRestless 你应该听从 mKorbel 的建议:发布SSCCE,人们可以在他们的机器上按原样运行,我相信你会得到一些答案。

标签: java swing nimbus jpopupmenu


【解决方案1】:

一种方法是为各个 JMenuItems 的背景着色并使其不透明:

JMenuItem a = new JMenuItem("A");
a.setOpaque(true);
a.setBackground(Color.GREEN);

然后给菜单本身一个绿色边框来填充其余部分:

menu.setBorder(BorderFactory.createLineBorder(Color.GREEN));

可能有一个简单/更直接的方法,但这对我有用。

【讨论】:

  • 补充@kleopatra 在下面写的内容(没有足够的代表甚至评论别人的帖子=/):设置ui.put("PopupMenu[Enabled].border", null); 使边框变为绿色。现在,我假设这是因为您正在吹走 LaF 边界画家,所以我犹豫是否建议将此作为解决方案。您可能想要做的是将它设置为一个自定义的 Painter ,它总是将它涂成绿色。不幸的是,Java 6(我正在运行的)中似乎存在访问限制,阻止我对此进行测试。不过这些似乎在 Java 7 中有所改变,因此值得一试。
  • 你应该能够通过更改 IDE 的访问限制规则甚至在 jdk6 中使用画家 - 可能不适用于生产代码,只是为了玩它
【解决方案2】:

不是全部 - 但看起来将菜单/项目的不透明度设置为 true 部分解决了它(正如 @Derek Richard 已经为在完全应用程序控制下创建的项目所做的那样):

UIDefaults ui = UIManager.getLookAndFeelDefaults();
ui.put("PopupMenu.background", GREEN);
ui.put("Menu.background", GREEN);
ui.put("Menu.opaque", true);
ui.put("MenuItem.background", GREEN);
ui.put("MenuItem.opaque", true);

等等,适用于所有类型的项目,例如单选按钮/复选框。

这仍然会留下一个上/下灰色区域 - 不知道哪个部分负责该绘图。去除边距有助于以看起来受到挤压的代价

// this looks not so good, but without the margins above/below are still grey
ui.put("PopupMenu.contentMargins", null);

教程中有一个list of property keys

【讨论】:

    【解决方案3】:
    • 两个答案都有一些错误

    • 上面提到的方法需要覆盖大多数对另一个 JComponents 及其颜色产生影响的 UIDeafaults

    • Nimbus 有自己的 Painter,就是一个例子……

    来自代码

    import com.sun.java.swing.Painter;
    import java.awt.*;
    import javax.swing.*;
    
    public class MyPopupWithNimbus {
    
        public MyPopupWithNimbus() {
            JFrame frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setSize(200, 200);
            JPanel panel = new JPanel(new BorderLayout());
            panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
            JList list = new JList();
            panel.add(list);
            frame.getContentPane().add(panel);
            JPopupMenu menu = new JPopupMenu();
            menu.add(new JMenuItem("A"));
            menu.add(new JMenuItem("B"));
            menu.add(new JMenuItem("C"));
            JMenu jmenu = new JMenu("D");
            jmenu.add(new JMenuItem("E"));
            menu.add(jmenu);
            frame.setVisible(true);
            menu.show(frame, 50, 50);
        }
    
        public static void main(String[] args) {
    
            try {
                for (UIManager.LookAndFeelInfo laf : UIManager.getInstalledLookAndFeels()) {
                    if ("Nimbus".equals(laf.getName())) {
                        UIManager.setLookAndFeel(laf.getClassName());
                        UIManager.getLookAndFeelDefaults().put("PopupMenu[Enabled].backgroundPainter",
                                new FillPainter(new Color(127, 255, 191)));
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            EventQueue.invokeLater(new Runnable() {
    
                @Override
                public void run() {
                    MyPopupWithNimbus aa = new MyPopupWithNimbus();
                }
            });
        }
    }
    
    class FillPainter implements Painter<JComponent> {
    
        private final Color color;
    
        FillPainter(Color c) {
            color = c;
        }
    
        @Override
        public void paint(Graphics2D g, JComponent object, int width, int height) {
            g.setColor(color);
            g.fillRect(0, 0, width - 1, height - 1);
        }
    }
    

    【讨论】:

    • +1 作为答案(如果不允许 java6 中的客户端代码访问 com.sun 包,则存在一个非常轻微的缺点)。只是想知道为什么默认画家在设置后不占用背景 - 它似乎没有画任何花哨的东西。
    • 最干净的方法。比其他两个复杂一点,但比我想象的要简单(与画家有关)。
    • 从可能的方式来看,这是一种常见且舒适的方式,Nimbus 还有一些其他动物园
    猜你喜欢
    • 2012-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-17
    • 2012-01-09
    • 1970-01-01
    • 1970-01-01
    • 2016-04-30
    相关资源
    最近更新 更多