问题
I have a JPanel parent with 3 JPanel children inside. They all currently make use of GridLayout and together represent a UML class. Problem is that when I add a new attribute or method, all 3 JPanel grow to the same size.
Desire behaviour is that:
- The title box's size always remain the same whenever a method or attribute is added.
- When a method is added, only the method panel grows and title and attribute panel's size stay the same.
- When an attribute is added, only the attribute panel grows and other panels size stay the same.
The parent JPanel already can automatically grow/shrink whenever a method/attribute is added. I'm toying around with GridBagLayout atm but I'm getting nowhere near the desire results.
Is there a simple (or simpler) way for me to solve this?!
Here is few pics to show my situation.
A newly created UML class =>
this is how it currently behaves =>But I want this =>
or this=> or this =>Edit2: added new pics for clarity. Im terribly sorry if original version was misleading.
Edit3: YESS! I have sorted it! Felt like forever!! Here is the SSEEC:
Child panel
import java.awt.Component;
import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.MouseEvent;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.event.MouseInputAdapter;
import javax.swing.event.MouseInputListener;
public class APanel extends JPanel{
private JTextField tf;
public APanel() {
this.setLayout(new GridLayout(0,1));
this.addMouseListener(mouseInputListener);
}
MouseInputListener mouseInputListener = new MouseInputAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
System.out.println("Adding a new text field!");
tf = MyTF.create("Double click");
addNewTF(tf);
Component source = (Component) e.getSource();
Container c = source.getParent();
while(true) {
if(c instanceof PPanel)
break;
else
c=c.getParent();
}
PPanel p = (PPanel) c;
p.expand();
}
};
public void addNewTF(JTextField tf) {
this.add(tf);
this.setSize(this.getWidth(), this.getHeight()+tf.getHeight());
this.revalidate();
this.repaint();
}
}
Parent panel:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class PPanel extends JPanel{
//private APanel panel1;
private JPanel panel1;
private APanel panel2;
private APanel panel3;
public PPanel() {
this.setLayout(new BoxLayout(this , BoxLayout.Y_AXIS));
this.setBackground(Color.black);
panel1 = new JPanel();
panel1.setLayout(new GridLayout(0,1));
panel1.add(new JTextField("title"));
panel2 = new APanel();
panel2.setBorder(BorderFactory.createLineBorder(Color.red));
panel3 = new APanel();
panel3.setBorder(BorderFactory.createLineBorder(Color.black));
this.add(panel1);
this.add(Box.createRigidArea(new Dimension(0,1)));
this.add(panel2);
this.add(panel3);
}
public void expand() {
this.setSize(this.getWidth(), this.getHeight()+33);
this.revalidate();
this.repaint();
}
public static void main(String[] args) {
JFrame frame = new JFrame();
PPanel panel = new PPanel();
panel.setBounds(10, 10, 100, 150);
JPanel c = new JPanel(null);
c.add(panel);
frame.add(c);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(new Dimension(350, 300));
frame.setTitle("Demo");
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
A helping class:
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.MouseEvent;
import javax.swing.JTextField;
import javax.swing.event.MouseInputAdapter;
import javax.swing.event.MouseInputListener;
public class MyTF {
public static JTextField create(String name) {
final JTextField tf = new JTextField(name);
System.out.println(tf.getPreferredSize());
tf.setPreferredSize(new Dimension(100,33));
tf.addMouseListener(mouseInputListener);
return tf;
}
static MouseInputListener mouseInputListener = new MouseInputAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
Component source = (Component) e.getSource();
Container c = source.getParent();
while(true) {
if(c instanceof PPanel)
break;
else if(c instanceof APanel)
{
c.dispatchEvent(e);
c = c.getParent();
break;
}
else
c=c.getParent();
}
c.dispatchEvent(e);
}
};
}
I gave up the efford playing with GridBagLayout, it was just too much for me. Then I tried borderLayout as suggested but couldn't get it to work like I want it either. Then finally BoxLayout, it should had worked but there was a bug in my code! So when I tried out 0verbose code suggest and playing around with it, it failed! Not until, I finished up the SSEEC, did the final compile and run it before I decided to post (I practically gave up at this point) then I realised that it worked...The panel that can grow in their own space, they dont interfere with each other.
I was like WTF!
Went back to my code and compared it SSEEC and there was a bug, the code to expand the height of the panel was in wrong place so they sort of like eating into each other spaces.
Better yet! I can specify the distance between the middle box with the box above and below it by one pixel. That means I can still use the trick by mKorbel to draw the back line seperating those boxes!
Edit 4: is there a way for me to set size of a component? If you run the SSEEC, you will notice that once the JTextField is added in, it's huge! It's bigger than the container...
回答1:
I suggest you to use BoxLayout. Here's a tutorial. Playing around with glues and rigid area you can obtain almost all desired layout. In your case the code should be somthing like this:
JPanel container = new JPanel();
container .setLayout(new BoxLayout(container , BoxLayout.Y_AXIS));
JPanel childTop = new JPanel();
JPanel childCenter = new JPanel();
JPanel childBottom = new JPanel();
childTop.setMaximumSize(...);
childBottom.setMaximumSize(...);
container.add(childTop);
container.add(Box.createVerticalGlue());
container.add(childCenter);
container.add(Box.createVerticalGlue());
container.add(childBottom);
When you need to insert a new child, remember to insert it into the right position: between one of the glues and childCenter. For example:
container.add(newChild, 2) ;
回答2:
maybe this by using BorderLayout you can do that
(example about basic stuff revalidate(); + repaint();
versus pack();
)
from code
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.LineBorder;
public class AddComponentsAtRuntime {
private JFrame f;
private JPanel panel;
private JCheckBox checkValidate, checkReValidate, checkRepaint, checkPack;
public AddComponentsAtRuntime() {
JButton b = new JButton();
b.setBackground(Color.red);
b.setBorder(new LineBorder(Color.black, 2));
b.setPreferredSize(new Dimension(600, 10));
panel = new JPanel(new GridLayout(0, 1));
panel.add(b);
JPanel panelnorth = new JPanel();
panelnorth.setPreferredSize(panel.getPreferredSize());
f = new JFrame();
f.setLayout(new BorderLayout(5, 5));
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(panelnorth,BorderLayout.NORTH);
f.add(panel,BorderLayout.CENTER);
f.add(getCheckBoxPanel(),BorderLayout.SOUTH);
f.setLocation(200, 200);
f.pack();
f.setVisible(true);
}
private JPanel getCheckBoxPanel() {
checkValidate = new JCheckBox("validate");
checkValidate.setSelected(false);
checkReValidate = new JCheckBox("revalidate");
checkReValidate.setSelected(false);
checkRepaint = new JCheckBox("repaint");
checkRepaint.setSelected(false);
checkPack = new JCheckBox("pack");
checkPack.setSelected(false);
JButton addComp = new JButton("Add New One");
addComp.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JButton b = new JButton();
b.setBackground(Color.red);
b.setBorder(new LineBorder(Color.black, 2));
b.setPreferredSize(new Dimension(600, 10));
panel.add(b);
makeChange();
System.out.println(" Components Count after Adds :" + panel.getComponentCount());
}
});
JButton removeComp = new JButton("Remove One");
removeComp.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int count = panel.getComponentCount();
if (count > 0) {
panel.remove(0);
}
makeChange();
System.out.println(" Components Count after Removes :" + panel.getComponentCount());
}
});
JPanel panel2 = new JPanel();
panel2.add(checkValidate);
panel2.add(checkReValidate);
panel2.add(checkRepaint);
panel2.add(checkPack);
panel2.add(addComp);
panel2.add(removeComp);
return panel2;
}
private void makeChange() {
if (checkValidate.isSelected()) {
panel.validate();
}
if (checkReValidate.isSelected()) {
panel.revalidate();
}
if (checkRepaint.isSelected()) {
panel.repaint();
}
if (checkPack.isSelected()) {
f.pack();
}
}
public static void main(String[] args) {
AddComponentsAtRuntime makingChanges = new AddComponentsAtRuntime();
}
}
回答3:
Unfortunately I am not 100% sure I understood your layout. It will be better if you can attach picture.
But if Grid Layout grows not as you want you can either use GridBagLayout that can do almost everything but a little bit complicated or use a combination of Border and other layouts.
Probably several border layout can help you. In this layout center is growing fast while north, south, west and east are growing slower. Try to use them.
Check BoxLayout too.
If you are going to create a lot of complicated dialog boxes check out MigLayout. Although some learning curve exists this layout can really help you to save hours and days.
Since you updated your question now I can definitely say that you can define BorderLayout, with North, South and Center.
Center will contain Grid layout with 2 rows. Each row will contain Border layeout again. The upper border layout will contain other pannel in the south. The lower border layout willcontaint border layout in the North.
Each one of that layouts will contain yet another border layouy with label in the west and text field in the center.
回答4:
You can use a GridBagLayout for this. I think what worked for me was
- add a fourth sub-panel to the top panel
- set the weighty for the top three panels to 0
- set the weighty for the bottom (new) panel to 1
- depending on what technique you are using to render the text in the sub-panels, you may need to set the preferred size of sub-panels explicitly.
- when you add new items to the sub-panels, you'll need to invalidate and re-layout the whole thing.
来源:https://stackoverflow.com/questions/7224072/java-which-layout-manager-suitable-for-this-task