4

What's the easiest way to make a JPanel that takes up a fixed percentage of its parent container, by width?

Its width should update when that of its parent container changes.

I tried using Box.createHorizontalStrut(), but that doesn't update when the width of the JPanel's parent container changes.

mKorbel
  • 109,525
  • 20
  • 134
  • 319
APerson
  • 8,140
  • 8
  • 35
  • 49

3 Answers3

7

What you want is a GridBagLayout. (How to use?)

Using a GridbagLayout allows you to define GridBagConstraints for each single component you add.

These constraints include weightx which does exactly what it says on the tin. This is a relative value calculated across all components in that "grid-row", similar to distribution parts.

Be aware that the weightx only begins to come into play, when the preferred size of components is exceeded. Be aware that this may lead to interesting bugs, when setting minimumSize(), see also this SO answer.

Anyways: you can get the value you need for weightx by specifying it's percentage across the components in it's current row, or any multiple of that.

This means for a component to take 50% of the available parent container width, use weightx = 0.5. make sure that the row's weightx values add up to 1.0

Community
  • 1
  • 1
Vogel612
  • 5,620
  • 5
  • 48
  • 73
  • Just be aware that the weightx only applies to extra space. This means the two components you add to the layout must already have a preferred size in the ratio you want to maintain. Then as the frame grow/shrinks the ratio will be maintained. – camickr Jun 03 '15 at 15:01
  • @camickr didn't I already say something to that effect? "Be aware that the weightx only begins to come into play, when the minimum size of components is exceeded." Or is what I said there actually incorrect, and the preferred size is the relevant measure? – Vogel612 Jun 03 '15 at 15:03
  • 1
    Yes the preferred size is the relevant measure. When you pack a frame the preferred size is used to size the frame. Then as the frame size changes the weightx ratio is used to change the widths in the appropriate ratio. – camickr Jun 03 '15 at 15:07
3

Another option is to use a SpringLayout:

import java.awt.*;
import javax.swing.*;

public class SpringLayoutTest {
  private JComponent makeUI() {
    SpringLayout layout = new SpringLayout();

    JPanel p = new JPanel(layout);
    p.setBorder(BorderFactory.createLineBorder(Color.GREEN, 10));

    Spring pw = layout.getConstraint(SpringLayout.WIDTH,  p);
    Spring ph = layout.getConstraint(SpringLayout.HEIGHT, p);

    JLabel l = new JLabel("label: 5%, 5%, 90%, 55%", SwingConstants.CENTER);
    l.setOpaque(true);
    l.setBackground(Color.ORANGE);
    l.setBorder(BorderFactory.createLineBorder(Color.RED, 1));

    JButton b = new JButton("button: 50%, 65%, 40%, 30%");

    setPercentage(layout.getConstraints(l), pw, ph, .05f, .05f, .90f, .55f);
    setPercentage(layout.getConstraints(b), pw, ph, .50f, .65f, .40f, .30f);

    p.add(l);
    p.add(b);
    return p;
  }

  private static void setPercentage(
      SpringLayout.Constraints c, Spring pw, Spring ph,
      float sx, float sy, float sw, float sh) {
    c.setX(Spring.scale(pw, sx));
    c.setY(Spring.scale(ph, sy));
    c.setWidth(Spring.scale(pw,  sw));
    c.setHeight(Spring.scale(ph, sh));
  }

  public static void main(String... args) {
    EventQueue.invokeLater(new Runnable() {
      @Override public void run() {
        createAndShowGUI();
      }
    });
  }
  public static void createAndShowGUI() {
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    frame.getContentPane().add(new SpringLayoutTest().makeUI());
    frame.setSize(320, 240);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
}
aterai
  • 9,658
  • 4
  • 35
  • 44
3

You could use the Relative Layout. This layout was designed for this purpose since none of the layouts from the JDK support this directly (and easily).

As a simple example you could have 3 panels at 25%, 25% and 50% of the width:

RelativeLayout rl = new RelativeLayout(RelativeLayout.X_AXIS);
rl.setFill( true );
JPanel panel = new JPanel( rl );
panel.add(red, new Float(25);
panel.add(green, new Float(25));
panel.add(blue, new Float(50));
camickr
  • 321,443
  • 19
  • 166
  • 288