Макет JPopupMenu: как обеспечить разный размер для пунктов меню в том же меню

Мне нужно два столбца пунктов меню в меню. Это может быть достигнуто с помощью GridBagLayout для всплывающего меню. Моя проблема: оба столбца имеют одинаковую ширину, которая является шириной самого большого пункта меню в меню (но не в столбце, как это должно быть для GridBagLayout). Когда я заменяю пункты меню в меню кнопками - раскладка работает правильно. Итак, как я могу посоветовать меню, что элементы должны иметь разную ширину?

Вот экраны:

Когда я использую пункты меню - оба столбца имеют одинаковую ширину (неправильно)Неправильная раскладка пунктов меню

Когда я использую кнопки - столбцы имеют разную ширину (правильно)Правильная раскладка для кнопок

Вот пример кода:

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.WindowConstants;

public class MenuTest {

    public static void main(String[] args) {
        JFrame frm = new JFrame("Menu test");
        JMenuBar menuBar = new JMenuBar();
        JMenu m1 = new JMenu("Menu items");
        m1.getPopupMenu().setLayout(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;
        m1.getPopupMenu().add(new JMenuItem("Very very very very long text"), gbc);
        gbc.gridx = 1;
        m1.getPopupMenu().add(new JMenuItem("Short text"), gbc);
        menuBar.add(m1);

        JMenu m2 = new JMenu("Buttons");
        m2.getPopupMenu().setLayout(new GridBagLayout());
        gbc.gridx = 0;
        m2.getPopupMenu().add(new JButton("Very very very very long text"), gbc);
        gbc.gridx = 1;
        m2.getPopupMenu().add(new JButton("Short text"), gbc);
        menuBar.add(m2);
        frm.setJMenuBar(menuBar);
        frm.add(new JScrollPane(new JTextArea("Sample text", 10, 40)));
        frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frm.pack();
        frm.setVisible(true);
    }
}

2 ответа

По-видимому, JMenuItem макет основан на максимальной ширине всего содержимого JMenutItem в том же родительском компоненте. Посмотрев на код Sun/Oracle, кажется, очень трудно вмешаться в это и предпочтительный размер каждого JMenuItem будет эквивалентно.

Тем не менее, я нашел один способ преодолеть это, переопределив getPreferredSize() из JMenutItemне красивая, но, кажется, работает:

import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;

import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingConstants;
import javax.swing.WindowConstants;

public class MenuTest {

    public static void main(String[] args) {
        JFrame frm = new JFrame("Menu test");
        JMenuBar menuBar = new JMenuBar();
        JMenu m1 = new JMenu("Menu items");
        m1.getPopupMenu().setLayout(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;
        JMenuItem comp1 = new JMenuItem("Very very very very long text") {
            @Override
            public Dimension getPreferredSize() {
                return new JMenuItem(getText(), getIcon()).getPreferredSize();
            }
        };
        m1.getPopupMenu().add(comp1, gbc);
        gbc.gridx = 1;
        JMenuItem comp2 = new JMenuItem("Short text") {
            @Override
            public Dimension getPreferredSize() {
                return new JMenuItem(getText(), getIcon()).getPreferredSize();
            }
        };
        m1.getPopupMenu().add(comp2, gbc);
        menuBar.add(m1);

        JMenu m2 = new JMenu("Buttons");
        m2.getPopupMenu().setLayout(new GridBagLayout());
        gbc.gridx = 0;
        m2.getPopupMenu().add(new JButton("Very very very very long text"), gbc);
        gbc.gridx = 1;
        m2.getPopupMenu().add(new JButton("Short text"), gbc);
        menuBar.add(m2);
        frm.setJMenuBar(menuBar);
        frm.add(new JScrollPane(new JTextArea("Sample text", 10, 40)));
        frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frm.pack();
        frm.setVisible(true);
        comp1.setVerticalTextPosition(SwingConstants.TOP);
        comp2.setVerticalTextPosition(SwingConstants.TOP);
    }
}

Конечно, если вы используете другие свойства JMenuItem, который изменяет пользовательский интерфейс JMenuItemвам нужно будет передать это "внутреннему" JMenuItem также. Это также, вероятно, трудно поддерживать и не будет работать на всех L&F.

Вы также можете, вероятно, улучшить это, повторно используя тот же "внутренний" JMenuItem через разные звонки.

Все, что я могу сказать, - это то, что JMenuItem имеет некоторую странную логику, когда дело доходит до вычисления размера пункта меню. Глядя на BasicMenuItemUI класс кажется, что MenuItemLayoutHelper Класс используется, чтобы помочь рассчитать предпочтительный размер пункта меню. Этот класс находится в sun.swing.* пакет, поэтому я не могу посмотреть на код.

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

import java.awt.*;
import javax.swing.*;

public class Main
{
    public static void main(String[] args) throws Exception
    {
        JMenuItem mi1 = new JMenuItem("Very very very long text");
        JMenuItem mi2 = new JMenuItem("Shorter text");
        JButton b1 = new JButton("Very very very long text");
        JButton b2 = new JButton("Shorter text");

        System.out.println("Menu Items:");
        createPanel(mi1, mi2);

        System.out.println("Buttons:");
        createPanel(b1, b2);

        System.out.println("mixture:");
        createPanel(mi2, b1);

    }

    public static void createPanel(Component c1, Component c2)
    {
        System.out.println(c1.getPreferredSize() + " : " + c2.getPreferredSize());

        JPanel panel = new JPanel( new GridBagLayout() );
        GridBagConstraints gbc = new GridBagConstraints();

        gbc.gridx = 0;
        gbc.gridy = 0;
        panel.add(c1, gbc);
        System.out.println("p1: " + panel.getPreferredSize());

        gbc.gridx = 1;
        panel.add(c2, gbc);
        System.out.println("p2: " + panel.getPreferredSize());
    }
}

Я получаю следующий вывод:

Menu Items:
java.awt.Dimension[width=153,height=21] : java.awt.Dimension[width=89,height=21]
p1: java.awt.Dimension[width=153,height=21]
p2: java.awt.Dimension[width=306,height=21]
Buttons:
java.awt.Dimension[width=166,height=26] : java.awt.Dimension[width=102,height=26]
p1: java.awt.Dimension[width=166,height=26]
p2: java.awt.Dimension[width=268,height=26]
mixture:
java.awt.Dimension[width=153,height=21] : java.awt.Dimension[width=166,height=26]
p1: java.awt.Dimension[width=89,height=21]
p2: java.awt.Dimension[width=255,height=26]

В примере "Пункты меню" второй пункт меню имеет ширину 89 однако, он волшебным образом получает ширину 153 (равной ширине с первым элементом меню) при добавлении на панель.

Пример "Кнопки" работает как положено. Предпочтительная ширина каждой кнопки (166 и 102) отражена в ширине панели 268.

В примере "Смесь" ширина пункта меню отображается как 153 ширина, которая была назначена при использовании в примере "пунктов меню". Однако, как только элемент меню будет добавлен на панель, его ширина уменьшится до 89 которая является ожидаемой шириной.

Это говорит мне о том, что предпочтительный размер пункта меню на самом деле является размером самого большого пункта меню, добавленного в контейнер.

Таким образом, если контейнер (JPanel или JPopupMenu) содержит только JMenuItems, то все пункты меню будут иметь одинаковый размер. Если контейнер содержит смесь элементов меню и других компонентов, то элемент меню будет отображаться в своем предпочтительном размере (конечно, в зависимости от менеджера компоновки).

Другие вопросы по тегам