Как я могу получить FlowLayout для выравнивания JPanels внизу, как это делается для других компонентов?

У меня есть случай, когда я добавляю JPanels к FlowLayout, и они не выравнивают себя в нижней части макета. Я использую это layout.setAlignOnBaseline(true) и это правильно выравнивает JLabels к нижней части панели. Однако после того, как эти ярлыки обернуты в сами панели, они больше не работают. Вот пример того, что я имею в виду, с двумя панелями сверху и снизу.

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);
        });
    }
}

Если вы запустите этот код, вы заметите, что верхняя панель имеет меньший "Foo" по центру на панели, а нижняя имеет желаемое "выровненное снизу" поведение, на которое я надеюсь. Любые идеи, как заставить sub JPanels вести себя так же?

3 ответа

Решение

API для setAlignOnBaseline(...) Метод утверждает:

Компоненты, не имеющие базовой линии, будут центрированы

JPanel не имеет разумного базового уровня для использования, так как компоненты могут быть в несколько строк в зависимости от используемого менеджера компоновки. Так что по центру.

По вашему вопросу я не могу сказать, действительно ли вы пытаетесь центрировать весь текст на базовой линии независимо от размера шрифта или просто пытаетесь нарисовать все компоненты в нижней части панели.

Если вы пытаетесь центрировать текст на базовой линии, то, возможно, вы можете переопределить базовую линию панели с помощью кода, например:

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

Конечно, это будет работать только в том случае, если все компоненты на панели имеют одинаковую базовую линию.

Или, если вы просто пытаетесь расположить все компоненты в нижней части панели, вам нужно использовать другой менеджер компоновки.

Вы можете использовать Относительную компоновку, чтобы выровнять все компоненты снизу.

Его можно использовать как прямую замену существующему коду:

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

Или, если вы не хотите использовать класс не JDK, то BoxLayout или GridBagLayout был бы способ пойти.

Если вы используете BoxLayout тогда вам нужно будет поиграть с setAlignmentY(...) свойство каждого компонента.

Если вы используете GridBagLayout, тогда вам нужно будет поиграть с ограничениями каждого компонента.

Сам я бы использовал для этого макет с большей "привлекательностью", чем у FlowLayout, такой как GridBagLayout. Если вы используете его, вы можете установить привязку SOUTHи весомым будет 0,0, что должно помешать компоненту растянуть его высоту и посадит его на дно. Например:

   JPanel topPanel = new JPanel();
    // topPanel.setLayout(layout);
    topPanel.setLayout(new GridBagLayout());
    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);

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = i;
        gbc.gridy = 0;
        gbc.weightx = 1.0;
        gbc.weighty = 0.0;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.anchor = GridBagConstraints.SOUTH;

        JPanel subPanel = new JPanel();
        subPanel.setLayout(new BorderLayout());
        subPanel.setBackground(Color.RED);
        subPanel.add(label);
        subPanel.setPreferredSize(subPanel.getMinimumSize());
        // subPanel.setAlignmentY(Component.BOTTOM_ALIGNMENT);   
        topPanel.add(subPanel, gbc);
    }

Подумав, как это можно сделать без GridBagLayout, я придумал это, хотя это немного "хакерски":

        JPanel topPanel = new JPanel();
        ((FlowLayout) topPanel.getLayout()).setAlignOnBaseline(true);
        topPanel.setBackground(Color.BLACK);

        int h = 0;
        for (int i = 0; i < 10; i++) {
            JLabel label = new JLabel("Foo");
            label.setForeground(Color.WHITE);
            label.setBackground(Color.GREEN);
            label.setOpaque(true);
            label.setFont(i % 2 == 0 ? font1 : font2);
            JPanel wrapBorder = new JPanel(new BorderLayout());
            JPanel subPanel = new JPanel();
            subPanel.setBackground(Color.RED);
            subPanel.add(label);
            subPanel.setBackground(Color.GREEN);
            wrapBorder.setOpaque(false);
            wrapBorder.add(BorderLayout.SOUTH, subPanel);
            if(wrapBorder.getPreferredSize().height > h) {
                h = wrapBorder.getPreferredSize().height;
            }
            topPanel.add(wrapBorder);
        }

        for(Component component : topPanel.getComponents()) {
            component.setPreferredSize(new Dimension(component.getPreferredSize().width, h));
        }

Единственная причина, по которой я это сделал, заключается в том, что лично я никогда не пользуюсь GridBagLayout, но у него есть свои плюсы (как видно;)).

Я могу просто упустить способ сделать JPanelРасширяемся вертикально, и я слишком много усложняю.

Другие вопросы по тегам