Reputation: 1367
That's what I did at first.
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);
}
If instead I replace the last 3 lines with this:
getContentPane().add(scrollPane);
everything is ok. But as I did before, the vertical scrollbar is not showing up. What is causing this? Is setting a JPanel as contentPane wrong?
Update: If contentPane changes to BorderLayout everything work fine.
// Here I create a JPanel to replace the contentPane of JFrame
JPanel contentPane = new JPanel(new BorderLayout());
contentPane.add(scrollPane);
setContentPane(contentPane);
So the problem is default FlowLayout?
Solved: The problem is FlowLayout. It wraps around JScrollPane and hides the Toolbars. using
scrollPane.setPreferredSize(new Dimension(500, 400)); // longer space in x-axis
solves it.
Answer: JSrollPane should not be used inside a Container that uses FlowLayout.
Upvotes: 0
Views: 1118
Reputation: 209132
Have a look at Rob Camick's WrapLayout, which is an extension of 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();
}
});
}
}
Upvotes: 1
Reputation: 11657
Firstly, what was the reason to create the second panel? There
is already the first panel that has BoxLayout
as the layout
manager set. Simply setting the scroll pane having the first panel
as parent works as expected.
You either call
setContentPane(scrollPane);
or
add(scrollPane);
Now I am going to explain what caused this unexpected behaviour. This is a quirk that sometimes happens to those who use nesting technique when building their layouts. When nesting is used, the layouts may influence each other.
By choosing another layout -- FlowLayout
-- as the underlying base layout,
you caused the first panel to be displayed in its preferred size. Instead
of one panel, you have now two panels, the base panel influences the
panel with labels -- it controls how it is sized. The FlowLayout
shows all its children in preferred size; it does not honour mimimum nor
maximum sizes. So the (first) visible panel is sized to show all its labels;
this is how preferred size is calculated -- just big enough to show all its
children. However, with 100 labels, it is very big; the layout is broken. It is
vertically so big that we cannot practically get to the bottom of the window.
So with our visible panel showing all its labels, what's the purpose of showing a verticall scrollbar? No need for one, since all labels are "visible" (placed on the window area), though the desing is broken.
So the problem does not lie with the scrollbars; they work normally. If you
set (in your example) the vertical scrollbar policy to VERTICAL_SCROLLBAR_ALWAYS
you will see the scrollbar there but without a slider, since all labels are "visible"
and there is nothing to be scrolled. (Scrollbars show items that are hidden from the
layout.) The issue lies in the fact that FlowLayout
shows its components in the preferred size only.
The following is a fixed code example that works as expected:
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);
}
});
}
}
Upvotes: 0
Reputation: 10153
First of all - there is nothing bad in using your own component as content pane. But default content pane is also a JPanel instance so there is actually no point to replace it with your own panel, unless you want to use non-panel content pane or customized panel component.
This is how the default content pane looks like:
/**
* 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;
}
This method is taken from JRootPane. It is basically a simple JPanel with a but customized layout manager as you can see.
Now, you have a few problems in your example.
First is the order of calls - you are sizing frame before adding content into it. Simply change the order and you will see your scrollpane:
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 );
}
} );
}
}
It will still look different because your new JPanel ()
uses FlowLayout
by default, instead of BorderLayout
used by default content pane component:
Simply set BorderLayout
and you will have the result you want to see:
JPanel contentPane = new JPanel ( new BorderLayout () );
Upvotes: 2