Reputation: 53
I am trying to make the contents of a JPanel
(the "parent panel") be added from the top towards the bottom, and have the width of each component match the width of the parent panel while having the height of each component wrap the content. The content will be dynamically generated.
This is what I'd like it to look like:
This would've been fairly easy to do in Android. I have looked into the Swing LayoutManagers, but none of them seemed to be an obvious choice. I have tried using Boxlayout
(vertical), which works except for the panels not filling the width of the parent panel (and this makes it look really bad).
Thankful for any advice!
Edit:
Thanks to camickr's answer, I figured it out. In case it would help anyone in the future, I have supplied my code and a screenshot.
The code that solved the question:
(Note the use of GridBagLayout, BorderLayout and the parameters for the GridBagConstraints)
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Random;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Test extends JFrame{
private final Color darkGreen = Color.decode("#388E3C");
private final Color green = Color.decode("#4CAF50");
private final Color lightGreen = Color.decode("#C8E6C9");
public static void main(String[] args){
Test test = new Test();
test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
int noOfTitles = 4;
int noOfSubTitles = 3;
Random random = new Random();
public Test(){
JPanel parentPanel = new JPanel();
this.setSize(new Dimension(300,800));
this.setContentPane(parentPanel);
parentPanel.setBackground(Color.white);
//Set layout of parent panel to Gridlayout
parentPanel.setLayout(new GridBagLayout());
//Panel for our titles
JPanel titlesPanel = new JPanel();
titlesPanel.setLayout(new BoxLayout(titlesPanel, BoxLayout.Y_AXIS));
for(int i = 0;i<noOfTitles;i++){
//Panel for the subtitles of each title
JPanel subTitlesPanel = new JPanel();
subTitlesPanel.setLayout(new BoxLayout(subTitlesPanel, BoxLayout.Y_AXIS));
for(int j = 0;j<noOfSubTitles;j++){
//Get a panel with a title header, a collapse/uncollapse button and some sample content
subTitlesPanel.add(getCollapsiblePanel(getSampleContent(),"Subtitle"));
subTitlesPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0));
}
//Get a panel with a title header, a collapse/uncollapse button and the subtitles as content
titlesPanel.add(getCollapsiblePanel(subTitlesPanel,"Title"));
}
//The constraints for the
GridBagConstraints constraints = new GridBagConstraints();
constraints.anchor = GridBagConstraints.PAGE_START;
constraints.fill = GridBagConstraints.HORIZONTAL; //Fill the panels horizontally. A weightx is needed for this to work.
constraints.gridx = 1;
constraints.gridy = 0;
constraints.weightx = 1; //Actually makes it fill
parentPanel.add(titlesPanel,constraints);
//To actually bump the rest of the other stuff to the top,
//we need something below with a weighty > 0
constraints.gridy = 1;
constraints.weighty = 1;
constraints.anchor = GridBagConstraints.PAGE_END;
parentPanel.add(new JLabel(""),constraints);
this.setVisible(true);
}
/*
* Gets a title for the supplied content-panel.
* The title includes the titleText and a button for collapsing/uncollapsing the content.
*/
private JPanel getCollapsiblePanel(JPanel content,String titleText){
JPanel titlePanel = new JPanel(); //Top container for the title
JPanel title = new JPanel(); //collapse/uncollapse button and title text
title.setLayout(new BoxLayout(title,BoxLayout.X_AXIS));
title.add(getToggleVisibilityIcon(content));
title.add(new JLabel(" "+titleText));
//A border layout is needed here for the fill of the parent panel to work
// (I tried with the box layout but it didn't work)
titlePanel.setLayout(new BorderLayout());
titlePanel.add(title,BorderLayout.PAGE_START);
titlePanel.add(content,BorderLayout.CENTER);
//Vanity
title.setBackground(green);
title.setBorder(BorderFactory.createEmptyBorder(1,1,2,1));
return titlePanel;
}
/*
* Not important, just generates a panel with some "sample" strings.
*/
private JPanel getSampleContent(){
JPanel content = new JPanel();
content.setLayout(new BoxLayout(content,BoxLayout.Y_AXIS));
for(int i = 0; i<1+random.nextInt(3); i++){
String sampleContent = "";
for(int j = 0;j<1 + random.nextInt(5);j++){
sampleContent += "sample ";
}
JLabel sample = new JLabel(sampleContent);
sample.setForeground(Color.decode("#111111"));
content.add(sample);
}
content.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
content.setBackground(lightGreen);
return content;
}
/*
* Method that returns a JLabel that will toggle the visibility of the
* supplied content panel upon clicking.
*/
private JLabel getToggleVisibilityIcon(JPanel content){
JLabel icon = new JLabel(" V ");
icon.setBackground(green);
icon.setBorder(BorderFactory.createLineBorder(darkGreen,2));
icon.setOpaque(true);
icon.addMouseListener(new MouseListener(){
private JPanel content;
private JLabel icon;
MouseListener init(JPanel content,JLabel icon){
this.content = content;
this.icon = icon;
return this;
}
@Override
public void mouseClicked(MouseEvent e) {
if(content.isVisible()){
content.setVisible(false);
icon.setText(" > ");
}else{
content.setVisible(true);
icon.setText(" V ");
}
}
@Override
public void mousePressed(MouseEvent e) {}
@Override
public void mouseReleased(MouseEvent e) {}
@Override
public void mouseEntered(MouseEvent e) {}
@Override
public void mouseExited(MouseEvent e) {}
}.init(content,icon));
return icon;
}
}
A screenshot of the running application:
Upvotes: 3
Views: 3000
Reputation: 324118
Not really understand your question. You generally don't have text, buttons and combo boxes randomly wrap. Usually the panel has a layout so the components are logically organized. That is you don't want a label with text like "First Name" followed by a text field that randomly wraps to the next line.
It looks to me like you have a "detail panel" that you hide or show?
Well you could implement this using a BorderLayout. So the hide/show button would be placed in the BorderLayout.PAGE_START. Then the detail panel would be in the BorderLayout.CENTER. Then you set the visibility of the detail panel as required.
Then the top level container could be a GridBagLayout
. You can use the constraints to have each row fill the width of the cell. Each child panel could use a Border
to make it slightly indents on the left side.
Read the Swing tutorial. There are sections on:
Upvotes: 1