4

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:

  1. The title box's size always remain the same whenever a method or attribute is added.
  2. When a method is added, only the method panel grows and title and attribute panel's size stay the same.
  3. 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 => enter image description here this is how it currently behaves => enter image description here

But I want this => enter image description here or this=>enter image description here or this =>enter image description here

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...

Community
  • 1
  • 1
bili
  • 610
  • 2
  • 9
  • 20
  • Throwing some sketches in might be useful, I'm having a little trouble visualizing what it is you want. – Jeffrey Aug 28 '11 at 21:35
  • There you go. Is it better now? – bili Aug 28 '11 at 21:43
  • JPanels at NORTH and SOUTH areas could be resized(decrease SIZE) if JPanel at CENTER area(on new Atribute adds and increase Size) – mKorbel Aug 28 '11 at 23:00
  • @ mKorbel: I'm testing out code that use BoxLayout suggested by 0verbose atm but need to play around a bit. I got two grey boxes, one between top and middle and one between middle and bottom box when use Box.createVerticalGlue(). I think the pics above did the bad job to illustrate it. The top box always remains the same size, well the height but the width can change when the whole panel is resized to a bigger one. The middle and bottom box can grow and shrink. – bili Aug 28 '11 at 23:06
  • don't forget to call revalidate() + repaint() as last lines, just to prevents against wrong output to the GUI, oooooor not necessary if you called pack(), nobody know, for better answer you have to in all cases post here you short runnable code that shows what you are tried, if not you just decreasing your chances of success (your bad) – mKorbel Aug 28 '11 at 23:16

4 Answers4

6

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) ;
Heisenbug
  • 38,762
  • 28
  • 132
  • 190
  • @kleopatra: why are you against setXXSize methods? could you explain me what's wrong with them? – Heisenbug Aug 29 '11 at 09:45
  • sure could I - just: what did you do to find out for yourself? As f.i. @bendicott did in a comment to my answer http://stackoverflow.com/questions/7074514/images-in-jtable-cells-off-by-one-pixel – kleopatra Aug 29 '11 at 10:10
  • @kleopatra: ok..thank you. I read the link. In that case you are using a JTable so you have used setRowHeight etc. But if I'm managing simple JLable, how can I specify such constraints without setXXSize methods? Maybe that's not to be discussed on comment, I'll post a question after reading something else about it. – Heisenbug Aug 29 '11 at 10:23
  • @kleopatra: if you have time and you want to, could you make an example related to the posted code? how can I achieve the same effect without setXXSize methods? Or my approach is completely wrong? – Heisenbug Aug 29 '11 at 10:25
  • with layouts the answer is always the same: use a suitable LayoutManager :-) – kleopatra Aug 29 '11 at 10:34
  • @0verbose: yes, BoxLayout was what I wanted! Thanks! – bili Aug 29 '11 at 14:41
  • @kleopatra: sorry for all the questions, but I stille have a doubt looking at your example: http://stackoverflow.com/questions/7074514/images-in-jtable-cells-off-by-one-pixel. setPreferredWidth is different from setPreferredSize? can we use this method? – Heisenbug Aug 31 '11 at 15:12
  • @Overbose: the technical answer is - a TableColumn is not a Component, so the setPref != setPref on components :-) Good question, though: with JTable being its own LayoutManager (kind of, in regard to sizing the columns) the column's getXXWidth sound like equivalents to comp.getXXSize. But they aren't by design: a) they have neither access to the rendering component nor to the data, so can do nothing to calculate a size b) the prefSize has an additional meaning of "what the user wants", it's updated by the table when the user resizes the column – kleopatra Aug 31 '11 at 16:17
  • @kleopatra: ok..thank you so much, and sorry again for the wasted time spent with me :) – Heisenbug Aug 31 '11 at 16:19
  • teaching the eager learner is never a waste, thanks for asking :-) – kleopatra Aug 31 '11 at 16:23
4

maybe this by using BorderLayout you can do that

(example about basic stuff revalidate(); + repaint(); versus pack();)

enter image description here enter image description here enter image description here

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();
    }
}
mKorbel
  • 109,525
  • 20
  • 134
  • 319
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.

AlexR
  • 114,158
  • 16
  • 130
  • 208
  • I have added in a few pics. See my OP. I'm not creating alot of boxes just one and trying to get it to behave well! – bili Aug 28 '11 at 21:46
1

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.
Burleigh Bear
  • 3,274
  • 22
  • 32