1

我知道这可能看起来像是重复的,但是通过尝试无效/验证/重新验证/重绘我没有得到任何结果,所以请多多包涵。我的面板结构如下所示:

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

……等等。在我的子面板(2)中,我更改了插图(右侧和底部),并且我想布局整个顶部面板(1)。是否有一些简单的方法来布局所有内容(最好从顶部面板(1)向下,但任何可行的方法都可以)。现在我已经在所有级别上尝试了无效/验证/重新验证/重新绘制的组合,但似乎没有任何效果(实际上根本没有任何变化)。谢谢!

编辑:我发现GridBagLayout克隆了GridBagConstraintsas 组件,所以我的代码无法通过运行 revalidate 和朋友工作的原因是我更新了错误的约束。我在下面找到并添加了解决此问题的方法。

4

3 回答 3

2

GridBagLayout克隆GridBagConstraintsas 组件被添加(这就是为什么改变我原来的一次对布局没有影响),所以我扩展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)布局工作。

于 2012-06-27T13:37:26.980 回答
2

仍然不完全确定您的确切上下文,但从您的“答案”来看,您似乎正在动态更改组件约束。要触发受影响部分的重新布局,您需要

  • 使约束已更改的子/人无效(注意:使父无效是不够的),这将根据需要使层次结构冒泡
  • 验证层次结构中的适当容器

片段:

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

编辑

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

好奇的我想知道层次结构中的重新验证如何导致有效大/子的重新布局:验证/树显然停在一个有效的组件上。下面是一个代码片段

  • 这是一个两级层次结构,一个有两个孩子的父母
  • 该动作改变了它脚下姐妹的布局影响属性(我们改变了布局的 v/hgap)并重新验证了父级

结果会有所不同,具体取决于父级的 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);
于 2012-06-27T14:29:43.570 回答
2
  • 整个 re_layout JFrame( JDialogei) 在那里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();
            }
        });
    }
}
于 2012-06-27T11:53:33.090 回答