Макет 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, то все пункты меню будут иметь одинаковый размер. Если контейнер содержит смесь элементов меню и других компонентов, то элемент меню будет отображаться в своем предпочтительном размере (конечно, в зависимости от менеджера компоновки).