Java динамически меняющиеся компоненты при появлении полосы прокрутки
Я пытаюсь реализовать функцию, которая (в моем тестовом проекте) после нажатия кнопки добавляет случайное число к моей JPanel. (Я использую макеты, которые у меня есть, потому что в моей настоящей программе у меня больше элементов внутри, и они отображаются правильно). Но мне нужно, чтобы моя программа распознала, когда полоса прокрутки видна (что я реализовал, но это небольшая задержка. Под задержкой я подразумеваю то, что я нажимаю кнопку, чтобы добавить число, если полоса прокрутки становится видимой, ничего не происходит. Но тогда в следующий раз, когда я нажимаю кнопку, она перемещается, как я хочу). Другая проблема, с которой я столкнулся (на которой я сейчас сосредоточен), заключается в том, что когда я динамически изменяю размер JPanel, если полоса прокрутки видна, я устанавливаю ее для изменения ширины на мою ширину - ширину полосы прокрутки., Но похоже, что когда полоса прокрутки видна, вновь введенное число перемещается в два раза больше ширины полосы прокрутки, чем один раз. Я был в этой части моей программы более дня и не могу понять это. Я добавлю свой полный код и несколько скриншотов.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class Main {
JFrame frame;
JPanel topPanel;
JPanel memoryPanel;
JScrollPane sPane;
JButton button;
ArrayList<Integer> list = new ArrayList<>();
boolean isVScrollVisible = false;
int scrollBarSize = 0;
public class MyChangeListener implements ChangeListener {
@Override
public void stateChanged(ChangeEvent e) {
isVScrollVisible = (sPane.getVerticalScrollBar().isVisible());
}
}
public class ButtonListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
Random random = new Random();
int r = random.nextInt(10);
list.add(r);
int n;
if (isVScrollVisible) {
n = scrollBarSize;
} else {
n = 0;
}
JPanel nextPanel = new JPanel();
nextPanel.setName("" + r);
nextPanel.setForeground(Color.BLACK);
nextPanel.setPreferredSize(new Dimension(200 - n, 55));
nextPanel.setMinimumSize(new Dimension(200 - n, 55));
nextPanel.setMaximumSize(new Dimension(200 - n, 55));
JPanel labelPanel = new JPanel();
labelPanel.setLayout(new BorderLayout());
JLabel label = new JLabel();
label.setText("" + r);
label.setPreferredSize(new Dimension(200 - n, 55));
label.setMinimumSize(new Dimension(200 - n, 55));
label.setMaximumSize(new Dimension(200 - n, 55));
label.setHorizontalAlignment(JLabel.RIGHT);
label.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 17));
label.setFont(new Font("Sans-Serif", Font.BOLD, 20));
labelPanel.add(label);
nextPanel.add(labelPanel, BorderLayout.LINE_START);
for (int i = 0; i < memoryPanel.getComponents().length; i++) {
memoryPanel.getComponent(i).setPreferredSize(new Dimension(200 - n, 55));
memoryPanel.getComponent(i).revalidate();
memoryPanel.getComponent(i).repaint();
}
memoryPanel.add(nextPanel, 0);
memoryPanel.revalidate();
memoryPanel.repaint();
sPane.revalidate();
sPane.repaint();
}
}
public Main() {
frame = new JFrame();
topPanel = new JPanel();
memoryPanel = new JPanel();
memoryPanel.setLayout(new BoxLayout(memoryPanel, BoxLayout.Y_AXIS));
sPane = new JScrollPane(memoryPanel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
sPane.setPreferredSize(new Dimension(200, 300));
sPane.getViewport().addChangeListener(new MyChangeListener());
scrollBarSize = ((Integer)UIManager.get("ScrollBar.width")) + 1;
button = new JButton("Add Random Number");
button.addActionListener(new ButtonListener());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
topPanel.add(button);
frame.add(topPanel, BorderLayout.PAGE_START);
frame.add(sPane, BorderLayout.CENTER);
frame.setResizable(false);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
new Main();
}
}
Мне нужно, чтобы они выглядели точно так же. До того, как у меня был код, который я имею сейчас, полоса прокрутки появлялась поверх цифр, которые выглядели ужасно. И причина, по которой у меня изменяемый размер фрейма - ложь, заключается в том, что в моей реальной программе я жестко закодировал все размеры, которые в будущем я буду вычислять правильные размеры, основываясь на размере кадра, поэтому прямо сейчас для параметра resizable в true нет вопрос. Любые предложения о том, что делать?
Это то, что я пытаюсь выполнить.
1 ответ
Избавьтесь от всей логики, которая устанавливает предпочтительные / минимальные / максимальные размеры. Каждый компонент знает, каким должен быть его размер. Каждый менеджер раскладки, в свою очередь, будет знать, каким должен быть предпочтительный размер панели. Позвольте менеджеру макета использовать информацию для выполнения своей работы.
Основная логика для динамического добавления компонентов:
panel.add(...);
panel.revalidate();
panel.repaint();
Тогда полосы прокрутки появятся автоматически при необходимости. Там нет необходимости для слушателей или что-нибудь.
Редактировать:
Причина, по которой я установил все размеры, состоит в том, что, если я их вынимаю, то все выглядит по центру
Узнайте, как правильно и эффективно использовать менеджеры компоновки.
Например, при использовании BoxLayout
Вы можете контролировать выравнивание компонентов, используя:
component.setAlignmentX(JLabel.RIGHT_ALIGNMENT);
и компонент будет выровнен по правому краю пространства, доступного для компонента.
При использовании JLabel вам также может понадобиться установить свойство в JLabel, чтобы выровнять текст по правому краю метки. Прочитайте JLabel API для соответствующего метода.