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

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

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

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

即使为嵌入式实例分配自己的PopupMenuUI也没有带来任何结果。 如果直接从JPopupMenuinheritance,则调用overriden paint(...)方法,但是,无论我做什么,都没有绘制任何内容。 如果inheritance自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(...)则会发生相同的事情( 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()); } }; 

这带来了

SecondScreen

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

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

按预期给出:

第三个屏幕

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

  • 两个答案都有一些错误

  • 和上面提到的方法来覆盖大多数影响另一个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 { 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); } } 

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

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

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

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

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

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

本教程中有一个属性键列表 。