I'm trying to develop a form of an accordion menu. There are a small number (2-12) options that can be toggled on/off. When toggled on, there will be a JPanel with additional settings that become visible. When toggled off, the additional settings will not be visible.
I've created a SelectableExpandablePanel class that extends JPanel and implements ActionListener and ComponentListener. The panel holds two things - a JToggleButton and a child Component (which will typically be a JPanel, but I don't want to limit myself for future reuse of this concept) in a BoxLayout to enforce one column. When the toggle button is selected, the child becomes visible. When the toggle is deselected, the child is hidden.
When I use this component, I intend to put it on a JPanel inside of a JScrollPane, as demonstrated in the sample main method.
There appear to be two problems that I'm having trouble overcoming:
If I don't specify a JFrame size, it's only large enough for the width of each child and tall enough for three buttons. When I click on the button, I would expect the JScrollPane to do its thing and generate a vertical scroll bar. This isn't happening.
I'd like the toggle buttons to be the full width of the JPanel that contains them. I thought what I did in the constructor plus the Component Listener would handle that, but it doesn't.
What is provided below compiles and has a main method. If compiled and executed, it drives the component I'm building to provide a test frame and the ability to reproduce the issues I'm talking about.
import javax.swing.BoxLayout;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToggleButton;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
public class SelectableExpandablePanel extends JPanel implements
ActionListener, ComponentListener {
private JToggleButton titleButton;
private JComponent childComponent;
public SelectableExpandablePanel(JComponent child) {
this(child, null, null);
}
public SelectableExpandablePanel(JComponent child, String title) {
this(child, title, null);
}
public SelectableExpandablePanel(JComponent child, String title,
String tooltip) {
super();
if (child == null) {
throw new IllegalArgumentException("Child component cannot be null");
}
childComponent = child;
titleButton = new JToggleButton();
titleButton.setText(title);
titleButton.addActionListener(this);
titleButton.setPreferredSize(new Dimension(getSize().width, titleButton
.getPreferredSize().height));
titleButton.setToolTipText(tooltip);
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
setPreferredSize(new Dimension(childComponent.getPreferredSize().width,
titleButton.getPreferredSize().height));
setSize(new Dimension(childComponent.getPreferredSize().width,
titleButton.getPreferredSize().height));
add(titleButton);
this.addComponentListener(this);
}
public void actionPerformed(ActionEvent e) {
if (titleButton.isSelected()) {
add(childComponent);
setSize(new Dimension(childComponent.getPreferredSize().width,
titleButton.getPreferredSize().height
+ childComponent.getPreferredSize().height));
} else {
remove(childComponent);
setSize(new Dimension(childComponent.getPreferredSize().width,
titleButton.getPreferredSize().height));
}
invalidate();
revalidate();
}
public void componentHidden(ComponentEvent arg0) {
// Do nothing
}
public void componentMoved(ComponentEvent arg0) {
// Do nothing
}
public void componentResized(ComponentEvent arg0) {
titleButton.setSize(this.getWidth(),
titleButton.getPreferredSize().height);
}
public void componentShown(ComponentEvent arg0) {
// Do nothing
}
public static void main(String[] args) {
JScrollPane scrollPane = new JScrollPane();
// These panels simulates a complex, multi-line configuration panel.
JPanel testPanel = new JPanel();
testPanel.setLayout(new BoxLayout(testPanel, BoxLayout.Y_AXIS));
testPanel.add(new JLabel("Test JLabel"));
testPanel.add(new JLabel("Test JLabel 2"));
testPanel.add(new JLabel("Test JLabel 3"));
JPanel testPanel2 = new JPanel();
testPanel2.setLayout(new BoxLayout(testPanel2, BoxLayout.Y_AXIS));
testPanel2.add(new JLabel("Test JLabel"));
testPanel2.add(new JLabel("Test JLabel 2"));
testPanel2.add(new JLabel("Test JLabel 3"));
JPanel testPanel3 = new JPanel();
testPanel3.setLayout(new BoxLayout(testPanel3, BoxLayout.Y_AXIS));
testPanel3.add(new JLabel("Test JLabel"));
testPanel3.add(new JLabel("Test JLabel 2"));
testPanel3.add(new JLabel("Test JLabel 3"));
// This panel simulates the panel that will contain each of the
// SelectableExpandablePanels.
JPanel testHolder = new JPanel();
testHolder.setLayout(new BoxLayout(testHolder, BoxLayout.Y_AXIS));
testHolder.add(new SelectableExpandablePanel(testPanel, "Test"));
testHolder.add(new SelectableExpandablePanel(testPanel2, "Test 2"));
testHolder.add(new SelectableExpandablePanel(testPanel3, "Test 3"));
// We add the test holder to the scroll pane. The intention is that if
// the expansion is too big to fit, the holding JFrame won't expand, but
// the scroll pane will get scroll bars to let the user scroll up and
// down through the toggle buttons and any enabled items.
scrollPane.setViewportView(testHolder);
JFrame testFrame = new JFrame("Expandable Panel Test");
testFrame.getContentPane().add(scrollPane);
testFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
testFrame.pack();
testFrame.setVisible(true);
}
}
Don't try to manage the sizes yourself:
//titleButton.setPreferredSize(new Dimension(getSize().width, titleButton.getPreferredSize().height));
titleButton.setToolTipText(tooltip);
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
//setPreferredSize(new Dimension(childComponent.getPreferredSize().width, titleButton.getPreferredSize().height));
//setSize(new Dimension(childComponent.getPreferredSize().width, titleButton.getPreferredSize().height));
Also, get rid of the setSize() code in the ActionListener. This will be ignored anyway as the layout manager will determine the size.
The scrollbars will appear when the preferred size of the panel is greater than the size of the scrollpane. If you hardcode the preferred size then you default the purpose of the layout manager and the preferred size won't change as you add/remove components.
Note for something like this I generally use a BorderLayout. Put the button in the PAGE_START and the other panel in the CENTER. The components will automatically fill the space available.
Remove all the setSize/setPreferredSize calls and let the LayoutManager do its thing.
To allow the JButtons to fill the width of the panel, you can use a BorderLayout (for instance, add the button to CENTER, and the child container to SOUTH and remove all those setSize values to let the LayoutManager handle it).
Related
This is the add(main) version
This is the add(scroll) version
Im trying to get a window full of lables and make it scrollable, this is my code for that purpose:
public class JobHistoryListScreen extends JFrame implements View
{
#Override
public void showScreen()
{
setSize(800, 800);
setLayout(new BorderLayout());
setDefaultCloseOperation(EXIT_ON_CLOSE);
JPanel main = new JPanel();
main.setSize(500,500);
JScrollPane scroll = new JScrollPane(main,JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scroll.setSize(500,500);
//Font
//Font david50 = new Font("David", Font.BOLD, 50);
for(int i=0; i<1000; i++)
{
JLabel empty = new JLabel("No jobs to display!");
empty.setBounds(0,i+250,400,100);
empty.setFont(david50);
main.add(empty);
}
add(main);
setVisible(true);
}
public static void main(String[] args) {
JobHistoryListScreen v = new JobHistoryListScreen();
v.showScreen();
}
}
For some reason the window gets filled with the labels but is not scrollable at all.
Learn about layout managers. Refer to Laying Out Components Within a Container. Default for JPanel is FlowLayout and because the JPanel is inside a JScrollPanel, the labels will not wrap. And since you set the horizontal scroll bar policy to NEVER, there is no horizontal scroll bar and hence you cannot scroll horizontally. Try using BoxLayout to display all the labels one under the other. Alternatively you could use a GridLayout with 0 (zero) rows and 1 (one) column. Refer to the tutorial for more details.
EDIT
Here is my modified version of your code. Explanatory notes appear after the code.
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.WindowConstants;
public class JobHistoryListScreen implements Runnable {
private JFrame frame;
#Override // java.lang.Runnable
public void run() {
showScreen();
}
public void showScreen() {
frame = new JFrame("Jobs");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JPanel main = new JPanel(new GridLayout(0, 1));
JScrollPane scroll = new JScrollPane(main,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scroll.setPreferredSize(new Dimension(500, 500));
Font david50 = new Font("David", Font.BOLD, 50);
for(int i=0; i<1000; i++) {
JLabel empty = new JLabel("No jobs to display!");
empty.setFont(david50);
main.add(empty);
}
frame.add(scroll);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
JobHistoryListScreen v = new JobHistoryListScreen();
// Launch Event Dispatch Thread (EDT)
EventQueue.invokeLater(v);
}
}
I don't know what interface View is so I removed that part.
No need to extend class JFrame.
No need to explicitly call setSize() on JFrame. Better to call pack().
Default content pane for JFrame is JPanel and default layout manager for that JPanel is BorderLayout so no need to explicitly set.
No need to call setSize() on JPanel.
Call setPreferredSize() rather than setSize() on JScrollPane.
Add the JScrollPane to the JFrame and not the JPanel.
No need to call setBounds() because GridLayout handles this.
Explicitly launch EDT (Event Dispatch Thread) by calling invokeLater().
Here is a screen capture of the running app. Note the vertical scroll bar.
I will place these buttons in the center of the frame and above each other, like this.
BUTTON
BUTTON
BUTTON
I've searched multiple topics on this forum but everything I tried didn't work for so far. I hope that somebody has the solution.
This is my code for so far:
package ípsen1;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JPanel;
public class Paneel extends JPanel implements ActionListener {
Image achtergrond;
private JButton spelHervatten;
private JButton spelOpslaan;
private JButton spelAfsluiten;
public Paneel(){
//buttons
spelHervatten = new JButton("Spel hervatten");
spelHervatten.setPreferredSize(new Dimension(380, 65));
spelOpslaan = new JButton("Spel opslaan");
spelOpslaan.setPreferredSize(new Dimension(380, 65));
spelAfsluiten = new JButton("Spel afsluiten");
spelAfsluiten.setPreferredSize(new Dimension(380, 65));
//object Paneel luistert naar button events
spelAfsluiten.addActionListener(this);
add (spelHervatten);
add (spelOpslaan);
add (spelAfsluiten);
}
public void paintComponent(Graphics g) {
//achtergrond afbeelding zetten
achtergrond = Toolkit.getDefaultToolkit().getImage("hout.jpg");
//screensize
g.drawImage(achtergrond, 0,0, 1024,768,this);
}
//actie na klik op button
public void actionPerformed(ActionEvent e) {
if(e.getSource() == spelAfsluiten){
System.out.println("Spel afsluiten");
System.exit(0);
}
}
}
You could use a GridBagLayout
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.fill = GridBagConstraints.HORIZONTAL;
add(new JButton("Button"), gbc);
add(new JButton("Button"), gbc);
add(new JButton("Button"), gbc);
add(new JButton("Button"), gbc);
See How to Use GridBagLayout for more details
A BoxLayout might be what you're after. You can specify that you want to add components along the y-axis in the constructor for that particular layout manager.
You could add this line to the constructor of your Paneel class.
this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
As for center-aligning everything, I don't know if it's good practice but you can set the horizontal alignment for each of your buttons individually. Example:
spelHervatten.setAlignmentX(CENTER_ALIGNMENT);
Uses a GridLayout for a single column of buttons of equal width.
The buttons stretch as the window's size increases. To maintain the button size, put the GridLayout as a single component into a GridBagLayout with no constraint. It will be centered.
The size of the buttons is increased by setting a margin.
import java.awt.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
/*
* Uses a GridLayout for a single column of buttons of equal width.
* The buttons stretch as the window's size increases. To maintain
* the button size, put the GridLayout as a single component into a
* GridBagLayout with no constraint. It will be centered.
*/
public class CenteredSingleColumnOfButtons {
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
// the GUI as seen by the user (without frame)
JPanel gui = new JPanel(new GridLayout(0,1,10,10));
gui.setBorder(new EmptyBorder(20,30,20,30));
String[] buttonLabels = {
"Spel hervatten",
"Spel opslaan",
"Spel afsluiten"
};
Insets margin = new Insets(20,150,20,150);
JButton b = null;
for (String s : buttonLabels) {
b = new JButton(s);
b.setMargin(margin);
gui.add(b);
}
JFrame f = new JFrame("Centered Single Column of Buttons");
f.add(gui);
// Ensures JVM closes after frame(s) closed and
// all non-daemon threads are finished
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// See http://stackoverflow.com/a/7143398/418556 for demo.
f.setLocationByPlatform(true);
// ensures the frame is the minimum size it needs to be
// in order display the components within it
f.pack();
f.setMinimumSize(f.getSize());
// should be done last, to avoid flickering, moving,
// resizing artifacts.
f.setVisible(true);
System.out.println(b.getSize());
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency
SwingUtilities.invokeLater(r);
}
}
I thought there is no way to do that.
You should get size of Panel/Frame then calculate manually to find to center position for your button.
Rephrased some parts:
You might want to try to put the buttons in JFrame's "wind direction"-style BorderLayout:
http://www.leepoint.net/notes-java/GUI/layouts/20borderlayout.html
Just create a block in the CENTER with one EAST and WEST block with a certain size around it. Then insert the buttons inside of the center block. If you don't want them to be the full size, just add another EAST and WEST.
I'm trying to align a JLabel to the right in a JPanel. I'm adding a JTabbedPane, a JPanel which contains my JLabel and JTextArea to a main JPanel.
I have searched SO and tried some methods like setAlignmentX, setHorizontalAlignment(SwingConstants.LEFT) and nested containers to no avail.
Here's my code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class LabelProblem
{
public static void main(String[] args)
{
JFrame frame = new JFrame("Label Problem");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel Main = new JPanel();
Main.setLayout(new BoxLayout(Main, BoxLayout.Y_AXIS));
JPanel ComponentPanel = new JPanel();
JLabel label = new JLabel("Sample Text");
label.setHorizontalAlignment(SwingConstants.LEFT);
label.setBorder(BorderFactory.createLineBorder(Color.BLACK));
label.setAlignmentX(Component.RIGHT_ALIGNMENT);
ComponentPanel.add(label);
JTabbedPane Tab = new JTabbedPane();
Tab.add("Document 1", new JPanel());
Main.add(Tab);
Main.add(ComponentPanel);
JTextArea Area = new JTextArea(10,10);
JScrollPane Scroll = new JScrollPane(Area);
frame.add(Main);
frame.add(Scroll, BorderLayout.SOUTH);
frame.setSize(450,450);
frame.setVisible(true);
}
}
How can I align my JLabel to the right?
Thanks!
So, the place of that label is determined by the layout of ComponentPanel. Since you didn't specify any layout it is using the default FlowLayout with a CENTER alignment. Assuming that you are ok with a FlowLayout it is a mere question of setting the alignment of the LEFT since this is possible with this layout.
Here's the code with the fix, however I suspect that as you put more elements to the ComponentPanel you will want to use another layout since FlowLayout is more adequate for menus and the like and not for displaying the main content.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
class LabelProblem
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
initGUI();
}
});
}
public static void initGUI()
{
JFrame frame = new JFrame("Label Problem");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel main = new JPanel();
main.setLayout(new BoxLayout(main, BoxLayout.Y_AXIS));
JPanel componentPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
JLabel label = new JLabel("Sample Text");
label.setBorder(BorderFactory.createLineBorder(Color.BLACK));
componentPanel.add(label);
JTabbedPane Tab = new JTabbedPane();
Tab.add("Document 1", new JPanel());
main.add(Tab);
main.add(componentPanel);
JTextArea area = new JTextArea(10, 10);
JScrollPane scroll = new JScrollPane(area);
frame.add(main);
frame.add(scroll, BorderLayout.SOUTH);
frame.setSize(450, 450);
frame.setVisible(true);
}
}
Result:
Note: I also changed the variable names to follow the java style convention: variable names should start with lower case to differenciate them from clases names, starting in upper case.
One simple approach is to set the label's horizontalAlignment to JLabel.RIGHT in the constructor.
import java.awt.*;
import javax.swing.*;
class LabelProblem {
public static void main(String[] args) {
JFrame frame = new JFrame("Label Problem");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridLayout(0, 1));
JTabbedPane tab = new JTabbedPane();
tab.add("Document 1", new JPanel());
frame.add(tab);
JLabel label = new JLabel("Sample Text", JLabel.RIGHT);
frame.add(label);
JTextArea area = new JTextArea(10, 10);
JScrollPane scroll = new JScrollPane(area);
frame.add(scroll);
frame.pack();
frame.setSize(450, 450);
frame.setVisible(true);
}
}
I think it may be a matter of you not actually setting layouts where you imagine you're setting layouts.
You have a JPanel with a vertically oriented BoxLayout (Main) enclosing another JPanel with default layout (ComponentPanel), finally enclosing your label. The reason why your label can't be pushed to the right is because is already is pushed to the right within it's enclosing container. If you set a colored border around ComponentPanel, you'll see what I mean -- it only occupies the same amount of space as the JLabel, giving the JLabel nowhere to move.
You need to set a layout and constraints for your intermediate ComponentPanel, allowing it to horizontally fill its parent container so that the label has someplace to go.
You haven't really specified how your layout is supposed to look, but if you change the layout on Main to X_AXIS, your label will pop over to the left (as will its parent container). Without knowing what you're really trying to do, I can't say much more.
I would however, suggest you throw your BoxLayout away entirely and look into using GridBagLayout, which gives you a high level control over your UI. GridBagLayout isn't the most concise construct, but that's the price of control.
Trying to change the look of a JOptionPane while its open, depending on which radiobutton the user clicks. What am I doing wrong? It works perfect if I for example add a button and move a JLabel from side to side of the window.
import java.awt.BorderLayout;
import java.awt.event.*;
import javax.swing.*;
import static javax.swing.JOptionPane.*;
public class ChangePanel extends JFrame{
private JButton click = new JButton("CLICK ME!");
ChangePanel(){
add(click, BorderLayout.SOUTH);
click.addActionListener(new ButtonListen());
setVisible(true);
setSize(300,100);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
}
public class ButtonListen implements ActionListener{
public void actionPerformed(ActionEvent e){
PopUpPanel pop = new PopUpPanel();
showConfirmDialog(ChangePanel.this, pop, "Changeable", OK_CANCEL_OPTION);
}
}
//Send this as Parameter to the ConfirmDialog
public class PopUpPanel extends JPanel implements ActionListener{
JRadioButton jewelry = new JRadioButton("Jewelry");
JRadioButton shares = new JRadioButton("Shares");
JRadioButton machine = new JRadioButton("Machine");
PopUpPanel(){
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
ButtonGroup bg = new ButtonGroup();
JPanel north = new JPanel();
bg.add(jewelry);
jewelry.addActionListener(this);
bg.add(shares);
shares.addActionListener(this);
bg.add(machine);
machine.addActionListener(this);
north.add(jewelry);
north.add(shares);
north.add(machine);
add(north);
}
//Listener for RadioButtons
public void actionPerformed(ActionEvent e){
JTextField info1Txt = new JTextField(12);
JTextField info2Txt = new JTextField(12);
JTextField info3Txt = new JTextField(3);;
JRadioButton b = (JRadioButton)e.getSource();
if(b.getText().equals("Jewelry")){
//Dummy test text
System.out.println("Jewelry");
JPanel info1 = new JPanel();
info1.add(new JLabel("info1:"));
info1.add(info1Txt);
add(info1);
JPanel info2 = new JPanel();
info2.add(new JLabel("info2:"));
info2.add(info2Txt);
add(info2);
JPanel info3 = new JPanel();
info3.add(new JLabel("info3:"));
info3.add(info3Txt);
add(info3);
validate();
repaint();
}else if(b.getText().equals("Shares")){
//Dummy test text
System.out.println("Shares");
}else
//Dummy test text
System.out.println("Machine");
}
}
public static void main(String[] args){
new ChangePanel();
}
}
As you are working with BoxLayout, you should provide size hints to the PopUpPanel panel, which you haven't given.
When a BoxLayout lays out components from top to bottom, it tries to size each component at the component's preferred height. If the vertical space of the layout does not match the sum of the preferred heights, then BoxLayout tries to resize the components to fill the space. The components either grow or shrink to fill the space, with BoxLayout honoring the minimum and maximum sizes of each of the components.
check out the official tutorial page discussion: BoxLayout Feature
Call revalidate() and repaint() on the container after removing or adding components to it. So if you change the following lines:
validate();
repaint();
to:
revalidate();
repaint();
The content should appear. Though, it will not fit the original size of the JOptionPane. You can override PopUpPanel.getPreferredSize() to return desired size so that JOptionPane is packed properly, ie:
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
You can also use JDialog instead of JOptionPane.
Also, consider using CardLayout instead of swapping components manually. Check How to Use CardLayout for examples.
Why not just use setPreferredSize(new Dimension(300, 300)) in PopUpPanel constructor? Works fine for me. Good eye on revalidate and repaint.
In java, I have been trying to create a panel that can accept other panels with a scroll bar.
I tried using gridlayout, and this works fine, except for the fact that if I only add a few panels, it grows those panels to fit the size of the parent panel.
I tried using flowlayout, but this makes the panels flow horizontally as there is a scroll bar.
How do I make it so I can add panels to the parent panel starting at the top and make them always the same size(or their preferred size).
Also, when I add panels to the parent panel after an event, they do not appear until after I move or resize the form. How do I make it repaint? calling repaint() on it did not work.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
/** This lays out components in a column that is constrained to the
top of an area, like the entries in a list or table. It uses a GridLayout
for the main components, thus ensuring they are each of the same size.
For variable height components, a BoxLayout would be better. */
class ConstrainedGrid {
ConstrainedGrid() {
final JPanel gui = new JPanel(new BorderLayout(5,5));
gui.setBorder(new EmptyBorder(3,3,3,3));
gui.setBackground(Color.red);
JPanel scrollPanel = new JPanel(new BorderLayout(2,2));
scrollPanel.setBackground(Color.green);
scrollPanel.add(new JLabel("Center"), BorderLayout.CENTER);
gui.add(new JScrollPane(scrollPanel), BorderLayout.CENTER);
final JPanel componentPanel = new JPanel(new GridLayout(0,1,3,3));
componentPanel.setBackground(Color.orange);
scrollPanel.add(componentPanel, BorderLayout.NORTH);
JButton add = new JButton("Add");
gui.add(add, BorderLayout.NORTH);
add.addActionListener( new ActionListener() {
public void actionPerformed(ActionEvent ae) {
componentPanel.add(new JTextField());
gui.validate();
}
});
Dimension d = gui.getPreferredSize();
d = new Dimension(d.width, d.height+100);
gui.setPreferredSize(d);
JOptionPane.showMessageDialog(null, gui);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ConstrainedGrid cg = new ConstrainedGrid();
}
});
}
}
Assuming JScrollPane, see Sizing a Scroll Pane. For convenience, Scrollable clients such as JTable offer setPreferredScrollableViewportSize(), but you can always set the viewport's size explicitly.