Swing and Nimbus: заменить фон JPopupMenu (прилагается к JMenu) - программирование
Подтвердить что ты не робот

Swing and Nimbus: заменить фон JPopupMenu (прилагается к JMenu)

Нимбус часто выглядит великолепно, но для определенных комбинаций цветов результат неоптимальный. В моем случае фон JPopupMenu не подходит, поэтому я хочу установить его вручную.

Я на Java 7, и, что интересно, Nimbus полностью игнорирует настройку некоторых свойств в UIManager (например, PopupMenu.background). Поэтому мой единственный вариант - создать подкласс JPopupMenu, который переопределяет paintComponent(...). Я знаю, что это противно, но, по крайней мере, это сработало.

Однако, если вы добавите JMenu в другое меню, он введет в него собственный экземпляр JPopupMenu, и я не мог понять, как заменить его своим собственным подклассом.

Даже присвоение встроенного экземпляра PopupMenuUI не принесло никаких результатов. Если унаследовано непосредственно из JPopupMenu, был вызван метод overriden paint(...), но неважно, что я сделал, ничего не было нарисовано. Если унаследовано от javax.swing.plaf.synth.SynthPopupMenuUI paint, даже не вызывается, и результат получается, если я вообще не установил собственный PopupMenuUI.

Итак, простой вопрос: как отрегулировать цвет фона одного JPopupMenu или (если это проще) все из них на Java 7 с помощью Nimbus как 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) или UIManager.getLookAndFeelDefautls().put(key,value) перед установкой L & F, но для меня это не приносит никаких результатов (что означает: никаких изменений в цветах по умолчанию вообще не происходит). Приведенный выше код, по крайней мере, приносит:

First screenshot

То же самое (что означает ничего) происходит, если вы используете 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);

дает, как и ожидалось:

Third screen

Вы можете получить этот JPopupMenu с помощью JMenu.getPopupMenu(), но вы не можете его установить. Даже переопределение этого метода в собственном подклассе JMenu не приводит к каким-либо результатам, так как JMenu, похоже, обращается к нему, включив экземпляр JPopupMenu без использования getter.

4b9b3361

Ответ 1

  • в обоих ответах есть несколько ошибок

  • и выше, чтобы потребовать переопределения большинства UIDeafaults, которые оказали влияние на другой JComponents и его цвет (ы)

  • У 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

Один из способов сделать это - цвет фона отдельных JMenuItems и сделать их непрозрачными:

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

Затем дайте самому меню зеленую рамку, чтобы заполнить остальные:

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

Там может быть легкий/более простой способ, но это сработало для меня.

Ответ 3

не вся история - но похоже, что непрозрачность меню/элементов истинно частично решает его (поскольку @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);

В учебнике содержится список ключей свойств.