Swing和Nimbus:替换JPopupMenu的背景(附加到JMenu)

时间:2012-07-23 23:43:53

标签: java swing nimbus jpopupmenu

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

我使用的是Java 7,有趣的是,Nimbus完全忽略了UIManager中某些属性的设置(如PopupMenu.background)。所以我唯一的选择是创建一个覆盖JPopupMenu的{​​{1}}子类。我知道,这很讨厌,但至少它起作用了。

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

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

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

编辑:代码示例

查看以下代码和结果:

JPopupMenu

我知道,有人说你应该在设置L& F之前使用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); } UIManager.put(key, value) ,但对我来说这不会带来任何结果(意思是:没有完全改变默认颜色)。上面的代码至少带来:

First screenshot

如果您使用UIManager.getLookAndFeelDefautls().put(key,value),则会发生相同的事情(无意义)。这是因为Nimbus使用内部画家,它根据Nimbus的原色计算颜色并忽略组件的属性。在此示例中,您可以使用以下方法作为解决方法:

JPopupMenu.setBackground(...)

带来了

SecondScreen

但是,如果您插入JPopupMenu menu = new JPopupMenu() { @Override public void paintComponent(final Graphics g) { g.setColor(Color.GREEN); g.fillRect(0,0,getWidth(), getHeight()); } }; 本身包装的JMenu无法覆盖,则此解决方法不起作用:

JPopupMenu

按预期给出:

Third screen

您可以使用JMenu jmenu = new JMenu("D"); jmenu.add(new JMenuItem("E")); menu.add(jmenu); 检索此JPopupMenu,但无法进行设置。即使在JMenu.getPopupMenu()的子类中重写此方法也不会产生任何结果,因为JMenu似乎在不使用getter的情况下访问了JMenu的包装实例。

3 个答案:

答案 0 :(得分:8)

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

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

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

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

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

答案 1 :(得分:8)

  • 两个答案都有一些错误

  • 以及上面提到的覆盖大多数影响另一个JComponents及其颜色的UIDeafault的方法

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

enter image description here

来自代码

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

答案 2 :(得分:7)

不是整个故事 - 但看起来将菜单/项目的不透明度设置为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);

等等,适用于所有类型的项目,如radioButton / checkBox。

这仍留下上/下灰色区域 - 不知道哪个部分负责该绘图。删除保证金有助于降低价格

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

教程中有一个list of property keys