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

2022-09-03 05:59:47

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

我在Java 7上,有趣的是,Nimbus完全忽略了(如)中某些属性的设置。因此,我唯一的选择是创建一个覆盖的子类。我知道,这很讨厌,但至少它有效。UIManagerPopupMenu.backgroundJPopupMenupaintComponent(...)

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

即使为嵌入式实例分配一个 own 也没有带来任何结果。如果直接从重写的方法被调用,但是,无论我做什么,都没有绘制任何内容。如果继承自甚至没有被调用,结果是如果我根本没有设置一个自己。PopupMenuUIJPopupMenupaint(...)javax.swing.plaf.synth.SynthPopupMenuUIpaintPopupMenuUI

所以简单的问题是:如何在Java 7上使用Nimbus作为L&F来调整一个或(如果更容易的话)所有背景的颜色?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)

First screenshot

如果您使用 ,则会发生同样的事情(意味着什么都不发生)。这是因为 Nimbus 使用内部画家,它根据 Nimbus 的原色计算颜色并忽略组件的属性。在此示例中,您可以使用以下方法作为解决方法:JPopupMenu.setBackground(...)

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

这就带来了

SecondScreen

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

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

正如预期的那样,

Third screen

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


答案 1

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

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

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

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

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


答案 2
  • 两个答案都有一些错误

  • 以及上面提到的覆盖大多数UIDeafaults的方式,这些UIDeafaults已经对另一个JComponents及其颜色产生了影响

  • Nimbus有自己的画家,一个例子...

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

推荐