Обновите шрифт JMenu и JMenu после того, как JMenuBar станет видимым

Следующий ниже код создает простой графический интерфейс с кнопкой в ​​центре, при нажатии которой должен обновляться шрифт компонентов JMenuBar. Для этого метод setMyFont запускается в ActionListener на JButton. Однако после нескольких перечисленных попыток мне это не удалось, но я не знаю, почему. Код, используемый в setMyFont, приведен ниже.

public void setMyFont(Font f, Font f2) {
    //Attempt 1 in the hope it would autodetect that font
    //had changed and just update
    menuFont = f;
    menuItemFont = f2;

    //Attempt 2 in the hope on the repaint it would update 
    //the font with the new UIManager properties
    UIManager.put("Menu.font", menuFont);
    UIManager.put("MenuItem.font", menuItemFont);

    //Attempt 3 in the hope that going over each component 
    //individually would update the font
    for(Component comp: getComponents()) {
        if(comp instanceof JMenu) {
            comp.setFont(menuFont);
        } else {
            comp.setFont(menuItemFont);
        }
    }

    validate();
    repaint();
}

Есть ли причина, по которой шрифт не обновляется на компонентах с текущим кодом? Кроме того, как я могу изменить свой код, чтобы шрифт обновлялся для компонентов, даже если они уже созданы?


Полный код для SSCCE

import java.awt.Component;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.UIManager;

public class Main extends JFrame {
    private static final long serialVersionUID = 3206847208968227199L;
    JButton but;
    MenuBar mB;

    private Main() {
        setSize(600, 600);

        mB = new MenuBar();
        setJMenuBar(new MenuBar());

        but = new JButton("Change Font");
        but.addActionListener(new CustomActionListener());
        add(but);

        setVisible(true);
        setLocationRelativeTo(null);
    }

    public static void main(String[] args) {
        new Main();
    }

    private class MenuBar extends JMenuBar {
        private static final long serialVersionUID = -2055260049565317972L;
        Font menuFont = new Font("Courier", Font.ITALIC + Font.BOLD, 12);
        Font menuItemFont = new Font("sans-serif", 0, 12);
        JMenu menu, subMenu;

        MenuBar() {
            UIManager.put("Menu.font", menuFont);
            UIManager.put("MenuItem.font", menuItemFont);

            menu = new JMenu("Menu");

            subMenu = new JMenu("Sub Menu");
            subMenu.add(new JMenuItem("Sub Item"));
            subMenu.add(new JMenu("Sub Menu"));
            menu.add(subMenu);

            menu.add(new JMenuItem("Sub Item"));
            menu.add(new JMenu("Sub Menu"));

            add(menu);

            menu = new JMenu("Another Menu");
            menu.add(new JMenu("Sub Menu"));
            menu.add(new JMenuItem("Sub Item"));
            menu.add(new JMenu("Sub Menu"));
            add(menu);
        }

        public void setMyFont(Font f, Font f2) {
            //Attempt 1 in the hope it would autodetect that font
            //had changed and just update
            menuFont = f;
            menuItemFont = f2;

            //Attempt 2 in the hope on the repaint it would update 
            //the font with the new UIManager properties
            UIManager.put("Menu.font", menuFont);
            UIManager.put("MenuItem.font", menuItemFont);

            //Attempt 3 in the hope that going over each component 
            //individually would update the font
            for(Component comp: getComponents()) {
                if(comp instanceof JMenu) {
                    comp.setFont(menuFont);
                } else {
                    comp.setFont(menuItemFont);
                }
            }

            validate();
            repaint();
        }
    }

    private class CustomActionListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            mB.setMyFont(new Font("sans-serif", 0, 12), new Font("Courier", Font.ITALIC + Font.BOLD, 12));
        }
    }
}

person Dan    schedule 14.07.2016    source источник


Ответы (3)


  1. После установки шрифта вам нужно, чтобы каждый компонент в иерархии вызывал обновление своего пользовательского интерфейса — SwingUtilities имеет для этого удобный метод

    UIManager.put("Menu.font",  menuFont);
    SwingUtilities.updateComponentTreeUI( Main.this );
    
  2. Используйте класс FontUIResource, например

    FontUIResource menuFont = new FontUIResource("Courier", Font.ITALIC + Font.BOLD, 12);
    

У меня работает следующий код, адаптированный из опубликованного SSCCE:

import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.FontUIResource;

public class Main extends JFrame {
    private static final long serialVersionUID = 3206847208968227199L;
    JButton but;
    MenuBar mB;

    private Main() {
        setSize(600, 600);

        mB = new MenuBar();
        setJMenuBar(mB);

        but = new JButton("Change Font");
        but.addActionListener(new CustomActionListener());
        add(but);

        setVisible(true);
        setLocationRelativeTo(null);
    }

    public static void main(String[] args) {
        new Main();
    }

    private class MenuBar extends JMenuBar {
        private static final long serialVersionUID = -2055260049565317972L;
        Font menuFont = new FontUIResource("Courier", Font.ITALIC + Font.BOLD, 12);
        Font menuItemFont = new FontUIResource("sans-serif", 0, 12);
        JMenu menu, subMenu;

        MenuBar() {
            UIManager.put("Menu.font", menuFont);
            UIManager.put("MenuItem.font", menuItemFont);

            menu = new JMenu("Menu");

            subMenu = new JMenu("Sub Menu");
            subMenu.add(new JMenuItem("Sub Item"));
            subMenu.add(new JMenu("Sub Menu"));
            menu.add(subMenu);

            menu.add(new JMenuItem("Sub Item"));
            menu.add(new JMenu("Sub Menu"));

            add(menu);

            menu = new JMenu("Another Menu");
            menu.add(new JMenu("Sub Menu"));
            menu.add(new JMenuItem("Sub Item"));
            menu.add(new JMenu("Sub Menu"));
            add(menu);
        }

        public void setMyFont(Font f, Font f2) {

            menuFont = f;
            menuItemFont = f2;
            UIManager.put("Menu.font", menuFont);
            UIManager.put("MenuItem.font", menuItemFont);
            SwingUtilities.updateComponentTreeUI(Main.this);
        }
    }

    private class CustomActionListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            mB.setMyFont(new FontUIResource("sans-serif", 0, 12), new FontUIResource("Courier", Font.ITALIC + Font.BOLD, 12));
        }
    }
}
person copeg    schedule 14.07.2016
comment
(1+) Использование updateComponentTree(...) проще, чем написание собственного рекурсивного метода для добавления всех компонентов в строку меню. Хотя вам нужно только вызвать метод в строке меню, а не во фрейме. - person camickr; 14.07.2016
comment
@camickr RE вызывается в строке меню в соответствии с документами, invoke the SwingUtilities updateComponentTreeUI method once per контейнер верхнего уровня - person copeg; 15.07.2016
comment
@copeg Спасибо за ответ. К сожалению, я, вероятно, делаю это неправильно, вызов SwingUtilities.updateComponentTreeUI( Main.this ); после установки шрифтов ничего не делает. Должен ли я выходить за пределы функции setMyFont или она должна работать. Я также внедрил FontUIResource, это ничего не изменило - person Dan; 15.07.2016
comment
@copeg, в общем, когда вы играете со свойствами UIManager, вы, вероятно, захотите сделать это на уровне фрейма, поскольку хотите, чтобы это влияло на все компоненты в фрейме. В этом случае, поскольку мы затрагиваем только компоненты, добавленные в строку меню, вам нужно только обновить дерево строки меню. Метод updateCompnentTreeeUI(...) принимает компонент в качестве параметра. Он просто выполняет рекурсивный поиск из этого компонента. - person camickr; 15.07.2016
comment
@Dan, after setting the fonts does not do anything. - метод updateComponentTreeUI(...) будет обновлять только свойства компонента, созданного с интерфейсом UIResource. Проблема с вашим опубликованным кодом заключается в том, что вы уже заменили шрифты по умолчанию в UIManager до того, как создали все свои компоненты Swing. Так что избавьтесь от всей этой логики. Вы должны обновлять только свойства UIManager в ActionListener - person camickr; 15.07.2016
comment
@camickr Спасибо за помощь - person Dan; 15.07.2016
comment
@copeg Спасибо за помощь - person Dan; 15.07.2016

    mB = new MenuBar();
    setJMenuBar(new MenuBar());

Ваш ActionListener не работает в строке меню, которую вы добавили во фрейм.

Код должен быть:

    mB = new MenuBar();
    //setJMenuBar(new MenuBar());
    setJMenuBar(mB);

Метод getComponents() не является рекурсивным. Таким образом, ваш цикл будет добавлять только компоненты JMenu в JMenuBar, а не JMenuItems.

Кроме того, при создании шрифтов почему бы не использовать шрифт большего размера, чтобы вы могли видеть, если что-то изменится.

Кроме того, в вашем опубликованном коде установка свойств шрифта UIManager перед созданием компонентов противоречит цели наличия кнопки. В вашем меню уже будет нужный шрифт.

person camickr    schedule 14.07.2016

Может быть, вы можете попробовать это:

    public class MenuFontChange {

    public static void changeMenuFont(JMenuBar jBar, Font font) {
        Component[] components = jBar.getComponents();
        for (Component component : components) {
            component.setFont(font);
            changeMenuItemFont((JMenu) component, font);
        }
    }

    public static void changeMenuItemFont(JMenu jMenu, Font font) {
        int n = jMenu.getItemCount();
        for (int i = 0; i < n; i++) {
            JMenuItem item = jMenu.getItem(i);
            item.setFont(font);
        }
    }
person W.Leto    schedule 19.05.2017