Glucose Glucose - 3 months ago 11
Java Question

How can I get FlowLayout to align JPanels at the bottom like it does for other components?

I have a case where I am adding JPanels to a FlowLayout, and they are not aligning themselves to the bottom of the layout. I'm using this

layout.setAlignOnBaseline(true)
and it properly aligns JLabels to the bottom of the panel. However, once those labels are wrapped in panels themselves it no longer works. Here is an example of what I mean, with two panels top and bottom.

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

public class BadLayout {

private static final Font font1 = new Font("Arial", Font.BOLD, 14);
private static final Font font2 = new Font("Arial", Font.BOLD, 30);

public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("Bad layout");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

FlowLayout layout = new FlowLayout(FlowLayout.LEADING, 0, 0);
layout.setAlignOnBaseline(true);

JPanel topPanel = new JPanel();
topPanel.setLayout(layout);
topPanel.setBackground(Color.BLACK);
for (int i = 0; i < 10; i++) {
JLabel label = new JLabel("Foo");
label.setForeground(Color.WHITE);
label.setBackground(Color.RED);
label.setOpaque(true);
label.setFont(i % 2 == 0 ? font1 : font2);

JPanel subPanel = new JPanel();
subPanel.setLayout(layout);
subPanel.setBackground(Color.RED);
subPanel.add(label);
subPanel.setAlignmentY(Component.BOTTOM_ALIGNMENT);

topPanel.add(subPanel);
}

JPanel bottomPanel = new JPanel();
bottomPanel.setLayout(layout);
bottomPanel.setBackground(Color.DARK_GRAY);
for (int i = 0; i < 10; i++) {
JLabel label = new JLabel("Foo");
label.setForeground(Color.WHITE);
label.setBackground(Color.RED);
label.setOpaque(true);
label.setFont(i % 2 == 0 ? font1 : font2);
bottomPanel.add(label);
}

JPanel parentPanel = new JPanel();
parentPanel.setLayout(new BorderLayout());
parentPanel.add(topPanel, BorderLayout.NORTH);
parentPanel.add(bottomPanel, BorderLayout.SOUTH);

frame.getContentPane().add(parentPanel);
frame.pack();
frame.setVisible(true);
});
}
}


If you run this code you'll notice the top panel has the smaller "Foo"s centered in the panel, while the one on the bottom has the desired 'bottom-aligned' behavior I'm hoping for. Any ideas how to get the sub JPanels to behave the same?

Answer

The API for the setAlignOnBaseline(...) method states:

Components that do not have a baseline will be centered

A JPanel does not have a reasonable baseline to use since components can be on multiple lines depending on the layout manager being used. So it is centered.

I can't tell from your question if you are actually trying to center all the text on the baseline independent of the Font size or whether you are just trying to get all components to be painted at the botton of the panel.

If you are trying to center text on the baseline then maybe you can override the baseline of the panel with code something like:

JPanel subPanel = new JPanel()
{
    @Override
    public int getBaseline(int width, int height)
    {
        Component c = getComponent(0);
        return c.getBaseline(width, height);
    }
};

Of course this would only work if all the components on the panel have the same baseline.

Or if you are just trying to position all the components on the bottom of the panel then you need to use a different layout manager.

You could use the Relative Layout to align all the components to the bottom.

It can be used as a direct replacement to your existing code:

RelativeLayout rl = new RelativeLayout(RelativeLayout.X_AXIS, 0);
rl.setAlignment( RelativeLayout.TRAILING );
JPanel topPanel = new JPanel(rl);

Or if you don't want to use a non JDK class then the BoxLayout or the GridBagLayout would be the way to go.

If you use a BoxLayout then you will need to play with the setAlignmentY(...) property of each component.

If you use the GridBagLayout, then you will need to play with the constraints of each component.

Comments