Панель, использующая 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.

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 используемый компонентом панели содержимого по умолчанию:

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

JPanel contentPane = new JPanel ( new BorderLayout () );


Взгляните на Rob Camick WrapLayout, который является расширением FlowLayout

import java.awt.*;
import javax.swing.*;

public class TestWrapLayout {
 public TestWrapLayout () {
 ImageIcon icon = new ImageIcon(getClass().getResource("/resources/stackoverflow2.png"));
 JPanel panel = new JPanel(new WrapLayout());
 for (int i = 1; i <= 250; i++) {
 JLabel iconlabel = new JLabel(icon);
 iconlabel.setLayout(new BorderLayout());
 JLabel textlabel = new JLabel(String.valueOf(i));
 textlabel.setHorizontalAlignment(JLabel.CENTER);
 textlabel.setForeground(Color.WHITE);
 textlabel.setFont(new Font("impact", Font.PLAIN,20));
 iconlabel.add(textlabel);
 panel.add(iconlabel);
 }
 JFrame frame = new JFrame();
 frame.add(new JScrollPane(panel));
 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 frame.setSize(300, 300);
 frame.setLocationRelativeTo(null);
 frame.setVisible(true);
 }

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


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

getContentPane().add(scrollPane);

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


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

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

setContentPane(scrollPane);

или

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);
 }
 });
 } 
}

licensed under cc by-sa 3.0 with attribution.