Java Swing递归布局

时间:2012-06-27 11:46:45

标签: java swing layout

我知道这可能看起来像是重复的,但我没有通过试验invalidate / validate / revalidate / repaint来获得任何地方,所以请耐心等待。我的面板结构看起来像这样:

JPanel/GridBagLayout (1)
+-- JPanel/BorderLayout
    +-- JPanel/GridBagLayout (2)
    |   +-- JTextField (3)
    |   +-- JComboBox
    +-- JPanel/WhateverLayout
    ...

......等等。在我的子面板(2)中,我更改了插图(右侧和底部),我想布局整个顶部面板(1)。是否有一些简单的方法来布局所有内容(最好从顶部面板(1)向下,但任何有效的东西都是okey)。现在我已尝试在所有级别上使用invalidate / validate / revalidate / repaint的组合,但似乎没有任何工作(事实上根本没有任何变化)。谢谢!

修改:我发现GridBagLayout克隆了GridBagConstraints,因为组件已添加,因此我的代码无法运行revalidate和朋友的原因是我更新了错误的约束。我在下面找到并添加了解决这个问题的方法。

3 个答案:

答案 0 :(得分:2)

  • re_layout整个JFrameJDialog e.i。)是JFrame#pack()

  • 表示容器中的填充可用区域(在remove / add之后)revalidate()repaint()

  • 您必须决定Components hierarchy

  • 中的哪些容器

关于pack()&的代码示例(re)validate()& repaint()

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
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);
        f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(panel, "Center");
        f.add(getCheckBoxPanel(), "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) {
        EventQueue.invokeLater(new Runnable() {

            public void run() {
                AddComponentsAtRuntime makingChanges = new AddComponentsAtRuntime();
            }
        });
    }
}

答案 1 :(得分:2)

GridBagLayout在添加组件时克隆GridBagConstraints(这就是为什么更改原始版本对布局没有影响的原因),因此我扩展GridBagLayout以便能够更新实际约束运行。下面的代码根据组件的类型设置布局边距,如果“扩展”,我用它在两种模式之间切换:

public class ExpandableGridBagLayout extends GridBagLayout {

    public void setExpand(boolean expanded) {
        for (Map.Entry<Component, GridBagConstraints> entry : comptable.entrySet()) {
            setExpandedMargin(entry.getKey(), entry.getValue(), expanded);
        }
    }

    private void setExpandedMargin(Component component, GridBagConstraints constraints, boolean expanded) {
        constraints.insets.right = 2;
        if (component instanceof JLabel) {
            constraints.insets.top = expanded ? 3 : 0;
            constraints.insets.bottom = expanded ? 3 : 0;
        } else {
            constraints.insets.bottom = expanded ? 8 : 5;
        }
    }
}

然后,我所要做的就是在panel.revalidate()上调用(1),布局按预期工作。

答案 2 :(得分:2)

仍然不完全确定您的确切背景,但从您的“答案”判断,您似乎正在改变组件约束。要触发受影响部件的重新布局,您需要

  • 使约束已被更改的子/ ren无效(注意: 足以使父项无效),这将根据需要冒充层次结构
  • 在层次结构中验证适当的容器

段:

ExpandableGridBagLayout bag = panel2.getLayout();
bag.setExpand(true);
for(Component child: panel2.getComponents())
    child.invalidate();
panel1.validate();

没有公共api以递归方式使给定容器下面的所有内容无效(invalidateTree是包私有)。快速入侵是暂时切换父容器的字体(内部消息invalidateTree)

/**
 * Invalidates the component hierarchy below the given container.<p>
 * 
 * This implementation hacks around package private scope of invalidateTree by 
 * exploiting the implementation detail that the method is internally 
 * used by setFont, so we temporary change the font of the given container to trigger
 * its internal call.<p>
 * 
 * @param parent
 */
protected void invalidateTree(Container parent) {
    Font font = parent.getFont();
    parent.setFont(null); 
    parent.setFont(font);
}

修改

不知道这个答案的哪一部分你的意思是不正确 - 显然我没有任何详细的知识就无法解决你的问题;-)

好奇的我想知道层次结构中的重新验证将如何导致有效的grand / children的重新布局:validate / Tree明显停在有效的组件上。以下是与

一起使用的代码段
  • 这是一个两级层次结构,一个有两个孩子的父母
  • 该操作会更改其脚下的姐妹的布局效果属性(我们更改布局的v / hgap)并重新验证父级

结果因f.i而异。在父

的LayoutManager上
  • 使用FlowLayout,没有任何事情发生,
  • 使用BoxLayout可以验证,具体取决于
    • 水平或垂直间隙是否已更改
    • 方向

看起来像一个有效的孩子的重新布局可能(或没有)更高的重新布局的副作用 - 没有任何保证发生和难以预测。没什么我想依靠的; - )

final JComponent sister = new JPanel();
final Border subBorder = BorderFactory.createLineBorder(Color.RED);
sister.setBorder(subBorder);
sister.add(new JTextField(20));
sister.add(new JButton("dummy - do nothing"));

JComponent brother = new JPanel();
brother.setBorder(BorderFactory.createLineBorder(Color.GREEN));
brother.add(new JTextField(20));
// vary the parent's LayoutManager
final JComponent parent = Box.createVerticalBox();
// final JComponent parent = Box.createHorizontalBox();
// final JComponent parent = new JPanel();
parent.add(sister);
parent.add(brother);

// action to simulate a change of child constraints 
Action action = new AbstractAction("change sub constraints") {

    @Override
    public void actionPerformed(ActionEvent e) {
        FlowLayout layout = (FlowLayout) sister.getLayout();
        layout.setHgap(layout.getHgap() * 2);
      //  layout.setVgap(layout.getVgap() * 2);
      //  sister.invalidate();
        parent.revalidate();
    }
};
brother.add(new JButton(action));

JFrame frame = new JFrame("play with validation");
frame.add(parent);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);