4

我有一个自定义的 swing 组件,它有一些子元素,包括 JTextField。我想为容器提供与 JTextField 相同的基线,因此我重写了容器中的 getBaseline 方法。但是,当为容器调用 getBaseline 时,并不总是设置 JTextField 的位置。我试图在容器的 getBaseline 方法中添加对 doLayout 的调用,但这没有帮助。我想要的是以下内容:

public int getBaseline(int w, int h) {
    Dimension size = textField.getPreferredSize();
    int textBaseline = textField.getBaseline(size.width, size.height);
    int textY = textField.getY();
    return textY + textBaseline;
}

在上面的代码中 textY 等于 0。

我写了一些代码来说明这个问题。第一次单击“添加”时,基线是错误的。第二次是正确的。

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;

class CustomComponent extends JPanel {

    public CustomComponent() {
        setLayout(new GridBagLayout());
        textField.setColumns(8);
        layoutElements();
    }

    public void addComponent() {
        JPanel comp = new JPanel();
        comp.setPreferredSize(new Dimension(50, 200));
        comp.setBackground(Color.red);
        otherComponents.add(comp);
        layoutElements();
    }

    public int getBaseline(int w, int h) {
        Dimension size = textField.getPreferredSize();
        return textField.getY() + textField.getBaseline(size.width, size.height);
    }

    public Component.BaselineResizeBehavior getBaselineResizeBehavior() {
        return Component.BaselineResizeBehavior.CONSTANT_DESCENT;
    }

    private void layoutElements() {
        removeAll();
        GridBagConstraints constraints = new GridBagConstraints();
        constraints.anchor = GridBagConstraints.SOUTH;
        add(textField, constraints);

        for (JPanel comp : otherComponents)
            add(comp, new GridBagConstraints());
        if (getParent() != null)
            getParent().validate();
    }

    private JTextField textField = new JTextField();
    private ArrayList<JPanel> otherComponents = new ArrayList<JPanel>();
}

public class Main {

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        JPanel panel = new JPanel();
        panel.setLayout(new GridBagLayout());
        frame.getContentPane().add(panel);

        JButton addComponent = new JButton("Add");
        GridBagConstraints constraints = new GridBagConstraints();
        constraints.anchor = GridBagConstraints.BASELINE;
        panel.add(addComponent, constraints);

        final CustomComponent customComp = new CustomComponent();
        panel.add(customComp, constraints);

        addComponent.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                customComp.addComponent();
            }
        });

        frame.setSize(400, 300);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}
4

1 回答 1

1

初步评论,只是一个确认:

可以确认外部布局的问题 GridBag(您的确切示例)或 MigLayout。如果外部布局是 FlowLayout,则可以修改。在这种情况下,在计算基线之前强制布局就足够了

@Override
public int getBaseline(int w, int h) {
    // helps with simple managers like FlowLayout
    // detoriates with powerful managers like GridBag or Mig
    doLayout();
    Dimension size = textField.getPreferredSize();
    return textField.getY() + textField.getBaseline(size.width, size.height);
}

在 main 的外部面板中:

FlowLayout flow = new FlowLayout();
flow.setAlignOnBaseline(true);
panel.setLayout(flow);

不多,但也许你可以从这里进一步挖掘......

编辑暂定解决方案(?或hack)和思考可能的原因

看起来它需要一个强制的两遍布局过程:

private void layoutElements() {
    removeAll();
    GridBagConstraints constraints = new GridBagConstraints();
    constraints.anchor = GridBagConstraints.SOUTH;
    add(textField, constraints);

    for (JPanel comp : otherComponents)
        add(comp, new GridBagConstraints());
    if (getParent() != null) {
        getParent().validate();
        getParent().revalidate();
    }
}

请注意验证与重新验证的顺序 - 由于某些原因,两者都是需要的。不知道确切的机制,只是随机猜测:

  • 布局发生自上而下,因此如果父级依赖于已布局所有孙子级的子级,则可以解释两遍的需要。与孙子相关的基线就是这样的条件
  • the first pass happens in a broader context, that is at the end of the first validate not all state is fully completed. Then a revalidate guarantees the second pass to happen after all the ongoing changes are completed.
于 2012-07-31T11:24:29.100 回答