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 для соответствующего метода.

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