Панель, использующая FlowLayout, не может содержать JScrollPanes?

Так я и поступил сначала.

public class MyFrame extends JFrame {

    public MyFrame() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setPreferredSize(new Dimension(500 ,300));
        setResizable(false);
        pack();
        setLocationRelativeTo(null);

        initComponents();
    }

    private void initComponents() {

        JPanel panel = new JPanel();
        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));

        for (int i=0; i < 100; i++)
            panel.add(new JLabel("some text"));

        JScrollPane scrollPane = new JScrollPane(panel, 
                ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, 
                ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);

        // Here I create a JPanel to replace the contentPane of JFrame
        JPanel contentPane = new JPanel();
        contentPane.add(scrollPane);
        setContentPane(contentPane);
    }

Если вместо этого я заменю последние 3 строки на это:

getContentPane().add(scrollPane);   

все хорошо. Но, как и раньше, вертикальная полоса прокрутки не отображается. Чем это вызвано? Неправильно ли установить JPanel как contentPane?

Обновление: если contentPane меняется на BorderLayout, все работает нормально.

// Here I create a JPanel to replace the contentPane of JFrame
JPanel contentPane = new JPanel(new BorderLayout());
contentPane.add(scrollPane);
setContentPane(contentPane);

Итак, проблема в FlowLayout по умолчанию?

Решено: проблема в FlowLayout. Он оборачивается вокруг JScrollPane и скрывает панели инструментов. с использованием

scrollPane.setPreferredSize(new Dimension(500, 400)); // longer space in x-axis

решает это.

Ответ: JSrollPane не следует использовать внутри контейнера, который использует FlowLayout.


person Stelium    schedule 11.07.2014    source источник
comment
pack() следует вызывать после добавления всех компонентов, особенно после вызова initComponents(). Чтобы быстрее получить более качественную помощь, опубликуйте MCVE (минимальный полный и проверяемый пример).   -  person Andrew Thompson    schedule 11.07.2014
comment
Для этой проблемы конкретное местоположение пакета не имеет значения. Я попробовал.   -  person Stelium    schedule 11.07.2014


Ответы (4)


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

Так выглядит панель содержимого по умолчанию:

/**
 * Called by the constructor methods to create the default 
 * <code>contentPane</code>. 
 * By default this method creates a new <code>JComponent</code> add sets a 
 * <code>BorderLayout</code> as its <code>LayoutManager</code>.
 * @return the default <code>contentPane</code>
 */
protected Container createContentPane() {
    JComponent c = new JPanel();
    c.setName(this.getName()+".contentPane");
    c.setLayout(new BorderLayout() {
        /* This BorderLayout subclass maps a null constraint to CENTER.
         * Although the reference BorderLayout also does this, some VMs
         * throw an IllegalArgumentException.
         */
        public void addLayoutComponent(Component comp, Object constraints) {
            if (constraints == null) {
                constraints = BorderLayout.CENTER;
            }
            super.addLayoutComponent(comp, constraints);
        }
    });
    return c;
}

Этот метод взят из JRootPane. Как видите, это простая панель JPanel с настраиваемым менеджером компоновки.

В вашем примере есть несколько проблем.

Во-первых, это порядок вызовов - вы изменяете размер кадра перед добавлением в него содержимого. Просто измените порядок, и вы увидите свою полосу прокрутки:

public class MyFrame extends JFrame
{
    public MyFrame ()
    {
        super();

        // Add components first
        initComponents ();

        // Setup frame after so it fits its new content
        setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
        setPreferredSize ( new Dimension ( 500, 300 ) );
        setResizable ( false );
        pack ();
        setLocationRelativeTo ( null );
    }

    private void initComponents ()
    {
        JPanel panel = new JPanel ();
        panel.setLayout ( new BoxLayout ( panel, BoxLayout.Y_AXIS ) );

        for ( int i = 0; i < 100; i++ )
        {
            panel.add ( new JLabel ( "some text" ) );
        }

        JScrollPane scrollPane =
                new JScrollPane ( panel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER );

        // Here I create a JPanel to replace the contentPane of JFrame
        JPanel contentPane = new JPanel ();
        contentPane.add ( scrollPane );
        setContentPane ( contentPane );
    }

    public static void main ( String[] args )
    {
        SwingUtilities.invokeLater ( new Runnable ()
        {
            public void run ()
            {
                new MyFrame ().setVisible ( true );
            }
        } );
    }
}

Он по-прежнему будет выглядеть по-другому, потому что ваш new JPanel () использует FlowLayout по умолчанию вместо BorderLayout, который используется компонентом панели содержимого по умолчанию:
  С FlowLayout

Просто установите BorderLayout, и вы получите желаемый результат:

JPanel contentPane = new JPanel ( new BorderLayout () );
person Mikle Garin    schedule 11.07.2014
comment
если я не использую BorderLayout. Итак, проблема в FlowLayout? - person Stelium; 11.07.2014
comment
FlowLayout не проблема - это просто другой макет, который по-разному размещает компоненты внутри контейнера. Тем не менее, это довольно странно, если пример, который я вам показал, не работает с FlowLayout, потому что он работает для меня. Убедитесь, что вы попробовали именно то, что я опубликовал. - person Mikle Garin; 11.07.2014
comment
Я также настоятельно рекомендую вам прочитать это руководство: docs.oracle.com/ javase / tutorial / uiswing / layout / visual.html. - person Mikle Garin; 11.07.2014
comment
Это довольно странно. Это работает, только если я использую BorderLayout, но не с FlowLayout. Я просто скопировал вставку в новый проект и получил то же самое. - person Stelium; 11.07.2014
comment
Я добавил снимок того, как мне выглядит пример кода (с FlowLayout). Я на всякий случай использую Win8.1 + JDK 1.6.0_30 .. - person Mikle Garin; 11.07.2014
comment
Для вашего редактирования: это то, о чем я говорил. Почему FlowLayout влияет на полосу прокрутки? - person Stelium; 11.07.2014
comment
Поскольку менеджер компоновки влияет на способ размещения компонентов (в данном случае JScrollPane) внутри контейнера (в данном случае - JPanel). Вот почему я сказал вам прежде, что вы должны сначала прочитать эту статью: docs.oracle.com/javase/tutorial/uiswing/layout/visual.html - я уверен, что он ответит на большинство ваших вопросов о макетах. - person Mikle Garin; 11.07.2014


Вы должны обновить фрейм после добавления панели с помощью функции pack(). Когда ты делаешь

getContentPane().add(scrollPane);

функция add сделает это за вас (ссылка)

person Hunsu    schedule 11.07.2014

Во-первых, по какой причине была создана вторая панель? Уже есть первая панель, для которой установлен BoxLayout в качестве менеджера по расположению. Простая установка панели прокрутки с первой панелью в качестве родительской работает должным образом.

Вы либо звоните

setContentPane(scrollPane);

or

add(scrollPane);

Теперь я объясню, что вызвало такое неожиданное поведение. Это причуда, которая иногда случается с теми, кто использует технику вложения при построении своих макетов. При использовании вложенности макеты могут влиять друг на друга.

Выбрав другой макет - FlowLayout - в качестве основного базового макета, вы заставили первую панель отображаться в ее предпочтительном размере. Вместо одной панели у вас теперь две панели, базовая панель влияет на панель с помощью меток - она ​​контролирует ее размер. FlowLayout показывает всех своих дочерних элементов в предпочтительном размере; он не соблюдает минимальные и максимальные размеры. Итак, (первая) видимая панель имеет размер, чтобы показать все ее метки; так рассчитывается предпочтительный размер - достаточно большой, чтобы показать все его дочерние элементы. Однако со 100 этикетками он очень большой; макет нарушен. По вертикали он такой большой, что мы практически не можем добраться до низа окна.

Итак, с нашей видимой панелью, показывающей все ее ярлыки, какова цель отображения вертикальной полосы прокрутки? В нем нет необходимости, так как все надписи «видимы» (размещаются в области окна), хотя дизайн нарушен.

Так что проблема не в полосах прокрутки; они работают нормально. Если вы установите (в вашем примере) политику вертикальной полосы прокрутки на VERTICAL_SCROLLBAR_ALWAYS, вы увидите полосу прокрутки там, но без ползунка, поскольку все метки «видимы» и прокручивать нечего. (Полосы прокрутки показывают элементы, которые скрыты от макета.) Проблема заключается в том, что FlowLayout показывает свои компоненты только в предпочтительном размере.

Ниже приведен пример фиксированного кода, который работает должным образом:

package com.zetcode;

import java.awt.Dimension;
import java.awt.EventQueue;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;

public class MyFrame extends JFrame {

    public MyFrame() {

        initComponents();

        setTitle("Scrollbar");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);                       
        setPreferredSize(new Dimension(300, 200));
        pack();
        setLocationRelativeTo(null);        

    }

    private void initComponents() {

        JPanel panel = new JPanel();
        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));

        for (int i=0; i < 100; i++)
            panel.add(new JLabel("some text"));

        JScrollPane scrollPane = new JScrollPane(panel, 
                ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, 
                ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);

        setContentPane(scrollPane);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                MyFrame ex = new MyFrame();
                ex.setVisible(true);
            }
        });
    }    
}

Пример shapshot

person Jan Bodnar    schedule 11.07.2014