Я пытаюсь поместить JPanel (GridLayout 0, 1) с JButtons в ViewPort JScrollPane. Это достаточно легко сделать, но при изменении размера родительского JFrame оно не работает «правильно». Размер кнопок изменяется, чтобы заполнить всю панель, как предполагалось, но с одним существенным ограничением. Если я попытаюсь изменить размер кадра по горизонтали (во время выполнения путем перетаскивания угла), он остановится после точки. Панель будет расширяться бесконечно, но не будет сжиматься ниже минимального размера, необходимого для отображения текста на кнопке с самым коротким текстом.
Изменение размера кадра по вертикали не вызывает такой проблемы. Рамка сожмется до нуля. Кнопки будут сжиматься до тех пор, пока не достигнут минимального размера, после чего они просто обрезаются за пределами рамки. Это то поведение, которое мне нужно, потому что я собираюсь поместить все это в JScrollPane, после чего появится полоса прокрутки. Я хочу того же поведения по горизонтали, но не могу добиться этого, как бы я ни старался.
До сих пор я пробовал использовать GridLayout и GridBagLayout. Последний кажется излишне тяжелым из-за всего лишь одного столбца кнопок. Однако, поскольку это столбец, я не могу использовать FlowLayout, поскольку он упорядочивает объекты по горизонтали, и я не знаю, как это изменить. Более того, и GridLayout, и GridBagLayout заполняют пространство и выравнивают кнопки так, как мне нравится, чего я не смог добиться с другими менеджерами макетов.
Эта проблема завела меня в столько тупиков, что я решил ограничить индивидуальный поток сознания в пользу единого изложения того, что произошло. Соответствующий пример кода выглядит следующим образом:
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.setSize(500, 500);
frame.setLocation(200, 200);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button1 = new JButton("Button 1");
JButton button2 = new JButton("------Button 2------");
JButton button3 = new JButton("Button 3");
JPanel buttonPanel = new JPanel(new GridLayout(0, 1));
buttonPanel.add(button1);
buttonPanel.add(button2);
buttonPanel.add(button3);
JButton button7 = new JButton("Button 1");
JButton button8 = new JButton("------Button 2------");
JButton button9 = new JButton("Button 3");
JPanel buttonPanel2 = new JPanel(new GridLayout(0, 1));
buttonPanel2.add(button7);
buttonPanel2.add(button8);
buttonPanel2.add(button9);
JScrollPane scrollPaneA = new JScrollPane();
scrollPaneA.getViewport().add(buttonPanel);
scrollPaneA.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
scrollPaneA.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
JScrollPane scrollPaneB = new JScrollPane();
scrollPaneB.getViewport().add(buttonPanel2);
scrollPaneB.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
scrollPaneB.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
JTabbedPane tabbedPane = new JTabbedPane();
tabbedPane.addTab("Left", scrollPaneA);
tabbedPane.addTab("Right", scrollPaneB);
JTextArea textArea = new JTextArea("Example Text");
JScrollPane textScrollpane = new JScrollPane();
textScrollpane.getViewport().add(textArea);
textScrollpane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
textScrollpane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
JButton button4 = new JButton("Button 4");
button4.addActionListener(new ButtonListener(tabbedPane, 0));
JButton button5 = new JButton("------Button 5------");
JButton button6 = new JButton("Button 6");
button6.addActionListener(new ButtonListener(tabbedPane, 1));
JPanel bottomPanel = new JPanel(new GridLayout(0, 1));
bottomPanel.add(button4);
bottomPanel.add(button5);
bottomPanel.add(button6);
JScrollPane rightScrollPane = new JScrollPane
(
bottomPanel,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED
);
JSplitPane rightSplitPane = new JSplitPane
(
JSplitPane.VERTICAL_SPLIT,
textScrollpane,
rightScrollPane
);
rightSplitPane.setDividerLocation(100);
JSplitPane mainSplitPane = new JSplitPane
(
JSplitPane.HORIZONTAL_SPLIT,
tabbedPane,
rightSplitPane
);
mainSplitPane.setDividerLocation(100);
frame.add(mainSplitPane);
frame.setVisible(true);
}
public class ButtonListener implements ActionListener
{
private JTabbedPane tabbedPane;
private int tab;
public ButtonListener(JTabbedPane tabbedPane, int tab)
{
this.tabbedPane = tabbedPane;
this.tab = tab;
}
@Override
public void actionPerformed(ActionEvent e)
{
JButton button1 = new JButton("Button ?");
JButton button2 = new JButton("------Button 2?------");
JButton button3 = new JButton("Button ?");
JPanel panel = new JPanel(new GridLayout(0, 1));
panel.add(button1);
panel.add(button2);
panel.add(button3);
this.tabbedPane.setComponentAt(this.tab, panel);
}
}
По сути, это то, что у меня было в моей программе. JTabbedPane начал прокручиваться правильно, как и следовало ожидать. Однако в тот момент, когда я редактировал любой из списков кнопок, прокрутка прервалась. Как вы можете видеть в прослушивателе, я просто заменяю всю панель, связанную с вкладкой. Как только я это сделал, JTabbedPane перестанет прокручиваться.
Я думал, что это вызвано тем, что минимальный размер JButtons каким-то образом переопределяет возможность прокрутки JScrollPane, отсюда и название вопроса. Хотя это и было фактором, реальную причину можно найти в ActionListener: this.tabbedPane.setComponentAt(this.tab, panel);
В моем дизайне каждая вкладка содержала свой собственный объект JScrollPane, который содержал JPanel в своем окне просмотра. На самом деле я помещал JPanel прямо на вкладку. Конечно, он не будет прокручиваться - я потерял JScrollPane, который должен был выполнять прокрутку.
Решение было до неприличия простым. Я только что исправил строку замены табуляции, чтобы установить область просмотра на JScrollPane, вместо того, чтобы заменять компонент на вкладке. По сути это:
public void actionPerformed(ActionEvent e)
{
JButton button1 = new JButton("Button ?");
JButton button2 = new JButton("------Button 2?------");
JButton button3 = new JButton("Button ?");
JPanel panel = new JPanel(new GridLayout(0, 1));
panel.add(button1);
panel.add(button2);
panel.add(button3);
//this.tabbedPane.setComponentAt(this.tab, panel);
JScrollPane scrollPane = (JScrollPane)this.tabbedPane.getComponentAt(tab);
scrollPane.getViewport().add(panel);
}
И теперь он работает именно так, как задумано. Это была полностью моя вина, что я заблудился в структуре и забыл, что вкладки должны содержать JScrollPanes, а не JPanels.
Обратите внимание, что панель прокрутки учитывает предпочтительный размер компонента, добавленного в фрейм. Таким образом, полосы прокрутки появляются, когда предпочтительный размер компонента, добавленного в область просмотра панели прокрутки, превышает размер панели прокрутки. Установка минимального размера панели с кнопками не влияет на прокрутку.
Проблема в том, что JScrollPane не сжимается меньше ~100 пикселей (в моем примере), независимо от того, какие предпочтительные размеры я использую. Если я их не использую или PreferredSizes меньше 100, горизонтальная полоса прокрутки никогда не появляется. Размер JFrame просто не может быть уменьшен.
1) Все еще не вижу изображения проблемы. 2) Вы изначально заявили, что размер рамки не будет меньше, чем необходимо для отображения текста на кнопках. Размер фрейма будет таким маленьким только потому, что в строке заголовка должны отображаться кнопки. Попробуйте использовать недекорированную рамку, чтобы увидеть разницу.
Ой! Итак, JFrame отказывается сжиматься из-за строки заголовка, а не из-за содержимого. В этом есть смысл, и я чувствую себя глупо из-за того, что не осознаю этого. Я попытался воссоздать то же поведение с помощью JSplitPane, и он может идти настолько далеко влево, насколько это необходимо, при этом полоса прокрутки отображается так, как задумано. Я не знаю, обязательно ли это решит мою проблему, но это дает мне возможность работать. Я соберу более крупный пример и при необходимости попрошу дополнительную помощь. Спасибо.
Я создал класс Java с вашим кодом.
Нет необходимости определять индивидуальный размер JButtons
JPanel
GridLayout
сделает их всех одинакового размера. Я добавил немного пробелов на панель кнопок, чтобы вы могли видеть синий фон.
По горизонтали я мог бы уменьшить JFrame
примерно до 100 пикселей. Я мог бы расширить JFrame
до ширины монитора.
По вертикали я мог бы сжать JFrame
до заголовка. Я мог бы расширить JFrame
до высоты моего монитора.
Я запустил код на Java 8.
Вот полный работоспособный код.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
public class JScrollPaneExample implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new JScrollPaneExample());
}
@Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JTabbedPane tabbedPane = new JTabbedPane();
JScrollPane scrollPane = new JScrollPane(createButtonPanel());
tabbedPane.addTab("Tab ", scrollPane);
frame.add(tabbedPane, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createButtonPanel() {
JPanel panel1 = new JPanel(new GridLayout(0, 1, 5, 5));
panel1.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
panel1.setMinimumSize(new Dimension(10, 10));
panel1.setBackground(Color.BLUE);
JButton button1 = new JButton("Button 1");
// button1.setPreferredSize(new Dimension(300,100));
panel1.add(button1);
JButton button2 = new JButton("Button 2---------------");
// button2.setPreferredSize(new Dimension(300,100));
panel1.add(button2);
JButton button3 = new JButton("Button 3");
// button3.setPreferredSize(new Dimension(300,100));
panel1.add(button3);
JButton button4 = new JButton("But");
// button4.setPreferredSize(new Dimension(300,100));
panel1.add(button4);
return panel1;
}
}
PreferredSize для кнопок был предназначен только для принудительной полосы прокрутки в рамках теста. Если я установлю предпочтительный размер выше 100 пикселей или около того, я смогу заставить их появиться. В противном случае JScrollPane перестанет сжиматься до PreferredSize. Проблема в том, что то, что у вас получилось, очень похоже на то, что получилось у меня. Я не могу сжать JScrollPane до размера менее 100 пикселей. И это не совсем 100. Однако их нужно много, чтобы содержать меньшие имена кнопок плюс некоторые отступы (я думаю). Либо так, либо есть какая-то универсальная константа, о которой я не знаю. Можно ли уменьшить его до размера менее 100 пикселей?
@Джордж Минков: Я не мог. Если вам нужно точное количество пикселей по горизонтали, создайте прослушиватель контейнера , чтобы распечатать размер JFrame
.
Я отредактировал ОП, проведя некоторые дополнительные эксперименты. Похоже, моя проблема заключалась в самой среде тестирования. Основываясь на другом комментарии, я попытался воспроизвести это поведение с помощью JSplitPane, и в этом не было ни одного нежелательного поведения. Проблема заключалась в минимальном размере строки заголовка JFrame. Теперь, чтобы реализовать это в моей программе. Спасибо.
Итак, после нескольких дней борьбы с этой проблемой и поиска нескольких вариантов, которые зашли в тупик, я наконец нашел причину проблемы - и с моей стороны это был PEBKAC. Это была не строка заголовка JFrame, не менеджеры по расположению, не минимальный размер.
Я размещал JButtons внутри JPanel внутри JScrollPane внутри вкладок JTabbedPane, который находится внутри одной стороны JSplitPane. При определенных событиях менялся список кнопок и перерисовывалась вся панель. В процессе перестройки JPanel я назначал его в качестве компонента вкладки JSPlitPane. Конечно, он не будет прокручиваться - я потерял JSPlitPane и просто использовал панель напрямую.
Изменение ActionListener для правильного назначения JSplitPane в качестве компонента для вкладки полностью устранило проблему. И я почувствовал себя очень глупо, потому что эта ошибка была основной...
Может быть, вы могли бы отредактировать свой ответ и опубликовать исправленный код, потому что, по крайней мере для меня, из вашего описания неясно, какие изменения вы внесли в свой код, чтобы решить проблему.
Я посмотрю что я могу сделать. Дело в том, что проблема оказалась за пределами примера, который я опубликовал. Это не было проблемой с JScrollPane, JPanel или GridLayout. Мне придется опубликовать несколько отрывков, чтобы объяснить.
Если вопрос не имеет отношения к вашей реальной проблеме, возможно, вам следует рассмотреть возможность удаления вопроса.
Что ж, проблема, с которой я столкнулся, оказалась в значительной степени не связанной с вопросом. Вы считаете, что в этом случае мне следует удалить свой вопрос? Это звучит справедливо. Могу ли я просто получить подтверждение, что это здесь стандартная практика?
Конечно, это стандартная практика. Альтернативно, отредактируйте свой вопрос так, чтобы он описывал вашу реальную проблему, для которой вы нашли решение.
Я получил довольно жёсткое автоматическое предупреждение об удалении вопросов с ответами на них с угрозой блокировки. Думаю, мне придется отредактировать ОП с новой информацией.
Я не понимаю вопроса. Рамка без проблем изменит размер по горизонтали. Если размер рамки станет меньше предпочтительного размера кнопок, появится полоса прокрутки. Я тестировал в Windows 11, используя JDK11. Возможно, это проблема версии/платформы. Опубликуйте изображение, показывающее то, что вы видите.