Jscrollpane изменение размера, содержащее JPanel при появлении полос прокрутки

у меня небольшая проблема при использовании JScrollPane в моем приложении Java.

у меня есть JScrollPane содержащий JPanel. Это JPanel динамически обновляется с кнопками (вертикальными), которые могут быть любой ширины. The JPanel автоматически регулирует свою ширину до самой большой JButton компонент внутри.

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

есть ли способ изменить размер моего JPanel когда появляется полоса прокрутки, так что она появляется рядом с моими кнопками? Или есть ли другой способ, чтобы полоса прокрутки появилась рядом с моим JPanel?

спасибо заранее!

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

import java.awt.BorderLayout;
import java.awt.EventQueue;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import java.awt.GridLayout;

/**
 * @author Dylan Kiss
 */

public class Demo {
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    JFrame myFrame = new JFrame("Demo");
                    JPanel sideBar = new JPanel();
                    JPanel centerPanel = new JPanel();
                    centerPanel.add(new JLabel("This is the center panel."));

                    JPanel buttonContainer = new JPanel();
                    JButton myButton = null;

                    for (int i = 0; i < 20; i++) {
                        buttonContainer.setLayout(new GridLayout(20, 1, 0, 0));
                        myButton = new JButton("This is my button nr. " + i);
                        buttonContainer.add(myButton);
                    }

                    sideBar.setLayout(new BorderLayout(0, 0));

                    JScrollPane scrollPane = new JScrollPane(buttonContainer);

                    sideBar.add(scrollPane);

                    myFrame.getContentPane().add(sideBar, BorderLayout.WEST);
                    myFrame.getContentPane().add(centerPanel, BorderLayout.CENTER);

                    myFrame.setLocationByPlatform(true);
                    myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    myFrame.pack();
                    myFrame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

3 ответов


вот простое, не очень, решение:

scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

изменить: Я подумал, что в твоем случае это может не сработать. Вот лучшее решение, хотя оно имеет довольно много шаблонных:

private class ButtonContainerHost extends JPanel implements Scrollable {
    private static final long serialVersionUID = 1L;

    private final JPanel buttonContainer;

    public ButtonContainerHost(JPanel buttonContainer) {
        super(new BorderLayout());
        this.buttonContainer = buttonContainer;
        add(buttonContainer);
    }

    @Override
    public Dimension getPreferredScrollableViewportSize() {
        Dimension preferredSize = buttonContainer.getPreferredSize();
        if (getParent() instanceof JViewport) {
            preferredSize.width += ((JScrollPane) getParent().getParent()).getVerticalScrollBar()
                    .getPreferredSize().width;
        }
        return preferredSize;
    }

    @Override
    public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
        return orientation == SwingConstants.HORIZONTAL ? Math.max(visibleRect.width * 9 / 10, 1)
                : Math.max(visibleRect.height * 9 / 10, 1);
    }

    @Override
    public boolean getScrollableTracksViewportHeight() {
        if (getParent() instanceof JViewport) {
            JViewport viewport = (JViewport) getParent();
            return getPreferredSize().height < viewport.getHeight();
        }
        return false;
    }

    @Override
    public boolean getScrollableTracksViewportWidth() {
        return true;
    }

    @Override
    public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
        return orientation == SwingConstants.HORIZONTAL ? Math.max(visibleRect.width / 10, 1)
                : Math.max(visibleRect.height / 10, 1);
    }
}

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

scrollPane = new JScrollPane(new ButtonContainerHost(buttonContainer));

мне кажется, что это обходное решение требуется из-за возможной ошибки в javax.качать.ScrollPaneLayout:

if (canScroll && (viewSize.height > extentSize.height)) {
    prefWidth += vsb.getPreferredSize().width;
}

здесь extentSize устанавливается в предпочтительный размер окна просмотра, а viewSize - в окно просмотра.getViewSize (). Это не кажется правильным, AFAIK размер представления внутри окна просмотра всегда должен равняться предпочтительному размеру. Мне кажется, что размер представления следует сравнивать с фактическим размером окна просмотра, а не с его предпочтительным размером.


простой обходной путь, чтобы удовлетворить ваши требования относительно

Is there a way to resize my JPanel when a scrollbar appears, so it 
appears nicely next to my buttons? 

использование EmptyBorder, это позволит вам достичь того, что вы чувствуете, должно произойти, как показано на рисунке ниже :

SCROLLBAR SETTING

Я просто добавил эту строку, написанную ниже после этой строки JPanel buttonContainer = new JPanel();

ДОБАВЛЕНА СТРОКА

buttonContainer.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));

вот ваш код с этой добавленной строкой:

import java.awt.BorderLayout;
import java.awt.EventQueue;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import java.awt.GridLayout;

/**
 * @author Dylan Kiss
 */

public class Demo 
{
    public static void main(String[] args) 
    {
        EventQueue.invokeLater(new Runnable() 
        {
            @Override
            public void run() 
            {
                try 
                {
                    JFrame myFrame = new JFrame("Demo");
                    JPanel sideBar = new JPanel();
                    JPanel centerPanel = new JPanel();
                    centerPanel.add(new JLabel("This is the center panel."));

                    JPanel buttonContainer = new JPanel();
                    buttonContainer.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
                    JButton myButton = null;

                    for (int i = 0; i < 20; i++) 
                    {
                        buttonContainer.setLayout(new GridLayout(20, 1, 0, 0));
                        myButton = new JButton("This is my button nr. " + i);
                        buttonContainer.add(myButton);
                    }

                    sideBar.setLayout(new BorderLayout(0, 0));

                    JScrollPane scrollPane = new JScrollPane(buttonContainer);      

                    sideBar.add(scrollPane);

                    myFrame.getContentPane().add(sideBar, BorderLayout.WEST);
                    myFrame.getContentPane().add(centerPanel, BorderLayout.CENTER);

                    myFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                    myFrame.pack();
                    myFrame.setLocationByPlatform(true);
                    myFrame.setVisible(true);
                } 
                catch (Exception e) 
                {
                    e.printStackTrace();
                }
            }
        });
    }
}

вы можете изменить размер JPanel, вызвав setPreferredSize, когда JPanel должен быть изменен.

buttonContainer.setPreferredSize(Dimension d);