JScrollPane 内的多个自定义 JPanel - resizing/showing 个组件上的一个组件全部调整大小

Multiple custom JPanels inside JScrollPane - resizing/showing components on one resizes them all

所以我有一个自定义 JPanel,我使用它的多个实例来填充 JScrollPane 内的包装面板。我使用的自定义 JPanel 元素的数量取决于列表的大小。我 运行 遇到的问题是我的自定义 JPanel 的一部分有另一个不可见的 JPanel,当我单击它的父级时它会展开。我试图模仿的行为是手风琴 UI 元素的行为。在我参与这个项目之前,我主要是一名网络开发人员,虽然我经常使用 Java,但我对 Swing 还是比较陌生。

这是一个行为示例 - 关闭所有元素的滚动窗格。 (请原谅我的快速油漆工作评论。我试图强调我看到的问题)。

接下来是第一个元素的图像展开 - 它意外地展开了所有其他元素。

必须注意的是,我只针对第一个面板并设置可见性,但是当我这样做时所有其他重复面板的长度都会增加,但显然内部组件保持不可见。

最后,这是我想要的最终结果:

JScrollPane 中是否有某种约束可以调整其子 JPanel 组件的大小以始终保持相同的高度?我似乎想不出解决这个问题的办法,我试过各种不同的包装器和布局,但都无济于事。

如果有人想查看代码片段,请告诉我,但由于项目的性质,必须对其进行大量编辑和精简。

谢谢, 马立克

PS: 是的,我绝对必须使用 Swing。

编辑:这是我的代码的一个静态的、快速的、精简的实现,正如 Frozen Peas 的 Roddy 所建议的

示例滚动窗格:

public class ExampleSrollPane extends JPanel {
private static ExampleSrollPane instance = null;
private JScrollPane contentScrollPanel = new JScrollPane();
private Vector<ExamplePanel> exPanels;
private JPanel wrapPanel = new JPanel();

public ExampleSrollPane() {
    super();
    this.setLayout(new BorderLayout());
    this.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED, Color.white,
            Color.white, new Color(115, 114, 105), new Color(165, 163, 151)));

    exPanels = new Vector<ExamplePanel>();
    init();
}

private void init() {

    contentScrollPanel.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
    contentScrollPanel.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
    contentScrollPanel.setBorder(new CompoundBorder(new EmptyBorder(5, 5, 5, 5), new SoftBevelBorder(BevelBorder.LOWERED)));
    this.add(contentScrollPanel, BorderLayout.CENTER);

    initPanels();
}

public void initPanels() {
    int numUnits = 15;
    // Init one empty panel at least
    if (numUnits == 0) numUnits = 15;
    wrapPanel.setLayout(new GridLayout(numUnits, 1));

    for (int i = 0; i < numUnits; i++) {
        ExamplePanel exPan = new ExamplePanel(i);
        exPanels.add(i, exPan);
        wrapPanel.add(exPan);
    }
    contentScrollPanel.setPreferredSize(new Dimension(575, 100));
    contentScrollPanel.getViewport().add(wrapPanel);
}
/**
 * Method: viewPanel()
 *
 */
private static void viewPanel() {
    JFrame frame = new JFrame();
    frame.setLayout(new BorderLayout());
    frame.setLocationRelativeTo(null);
    frame.add(getInstance());
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.pack();
    frame.setSize(new Dimension(600, 350));
    frame.setAlwaysOnTop(true);
    frame.setVisible(true);
}

public static ExampleSrollPane getInstance() {
    if (instance == null) {
        instance = new ExampleSrollPane();
    }
    return instance;
}
/**
 * The main method.
 * 
 * @param args the arguments
 */
public static void main(String args[]) {
    java.awt.EventQueue.invokeLater(new Runnable() {
        public void run() {
            viewPanel();
        }
    });
}

}

它在 showHideTable 方法中产生了问题。

示例面板(我的自定义 JPanel):

public class ExamplePanel extends JPanel implements ActionListener {
private static final long serialVersionUID = 1L;

private static final Border STAT_BORDER = BorderFactory.createBevelBorder(BevelBorder.LOWERED, Color.white,
        Color.white, new Color(115, 114, 105), new Color(165, 163, 151));

public static final EmptyBorder PAD_BORDER = new EmptyBorder(10, 10, 10, 10);

public int indx;
private JLabel unitLabel;
private JLabel statLabel;
private JLabel invLabel;
private JLabel targetLabel;
private JLabel timeLabel;

// Custom BasicArrowButton to expand/hide the "table"
private UnitToggleButton unitToggleButton;
// The expandable JPanel
public ExpanableTable elementTable;

private String id;
private String unitStatusString;
private String invStatusString;
private String targetString;
private String timeString;

public Color componentColor;

private JPanel topPanel = new JPanel();
public JPanel tablePanel = new JPanel();

public ExamplePanel(int index) {
    super();
    this.indx = index;
    id = "Unit # 00000";
    id = "Unit #00000";
    unitStatusString = "PENDING";
    invStatusString = "PENDING";
    elementTable = new ExpanableTable();
    targetString = "AZ501";
    timeString = "11:18:27";
    componentColor = this.getBackground();
    init();

}

private void init() {
    topPanel.setLayout(new GridBagLayout());
    topPanel.setBorder(PAD_BORDER);

    unitLabel = new JLabel(id); // TODO unit.getID();
    statLabel = new JLabel(unitStatusString); // TODO: unit.getStatus();
    invLabel = new JLabel(invStatusString); // TODO: unit.getInventoryStatus();

    targetLabel = new JLabel(targetString);
    timeLabel = new JLabel(timeString);

    buildLabel(statLabel);
    buildLabel(invLabel);
    buildLabel(targetLabel);
    buildLabel(timeLabel);

    unitToggleButton = new UnitToggleButton(BasicArrowButton.EAST, indx);

    GridBagConstraints gbc = new GridBagConstraints();
    gbc.anchor = GridBagConstraints.FIRST_LINE_START;
    gbc.fill = GridBagConstraints.FIRST_LINE_END;
    gbc.gridx = 0;
    gbc.gridy = 0;
    gbc.weightx = .1;
    gbc.weighty = 1;
    gbc.insets = new Insets(0, 0, 5, 0);

    // Add toggle button far-left, row 1
    topPanel.add(unitToggleButton, gbc);

    // Add empty space far-left, row 2
    gbc.gridy = 1;
    topPanel.add(new JLabel("     "), gbc);

    // Add unit label row 1 column 2
    gbc.gridy = 0;
    gbc.gridx = 1;
    gbc.weightx = .3;
    topPanel.add(unitLabel, gbc);

    // Add Status label row 1 column 3
    gbc.gridx = 2;
    topPanel.add(statLabel, gbc);

    // Add inventory label row 1 column 4
    gbc.gridx = 3;
    topPanel.add(invLabel, gbc);

    // Add tasking label row 2 column 2
    gbc.gridy = 1;
    gbc.gridx = 1;
    topPanel.add(new JLabel("   Tasking: "), gbc);

    // Add target label row 2 column 3
    gbc.gridx = 2;
    topPanel.add(targetLabel, gbc);

    // Add mission Label row 2 column 4
    gbc.gridx = 3;
    topPanel.add(timeLabel, gbc);

    gbc.gridx = 0;
    gbc.gridy = 2;
    gbc.weighty = 1;
    gbc.weightx = 1;
    gbc.gridwidth = 4;
    gbc.fill = GridBagConstraints.HORIZONTAL;
    gbc.insets = new Insets(0, 0, 0, 0);
    JSeparator sep = new JSeparator(JSeparator.HORIZONTAL);
    topPanel.add(sep, gbc);

    gbc.gridy = 3;
    topPanel.add(elementTable, gbc);
    revalidate();

    this.setLayout(new BorderLayout());
    this.add(topPanel, BorderLayout.NORTH);
    this.add(tablePanel, BorderLayout.CENTER);
    HSIUtils.setColoredBorder(tablePanel, Color.RED);
    tablePanel.add(elementTable);

    // Do NOT show the table on initialization
    tablePanel.setVisible(false);
    unitToggleButton.addActionListener(this);
}

/**
 * Method: buildLabel()
 *
 * @param label
 */
private void buildLabel(JLabel label) {
    label.setBorder(STAT_BORDER);
    label.setMinimumSize(new Dimension(80, 20));
    label.setPreferredSize(new Dimension(100, 25));
    label.setOpaque(true);
    label.setHorizontalAlignment(SwingConstants.CENTER);
    label.setHorizontalTextPosition(SwingConstants.CENTER);
    label.setBackground(componentColor);
}

private void showHideTable(boolean show) {
    tablePanel.setVisible(!show);
}

@Override
public void actionPerformed(ActionEvent e) {
    if (e.getSource() == this.unitToggleButton) {
        showHideTable(unitToggleButton.isExpanded());
    }

}

}

可扩展表:

public class ExpanableTable extends JPanel {

    public ExpanableTable () {
        super();
        this.setLayout(new BorderLayout());
        add(new JButton("Test1"), BorderLayout.WEST);
        add(new JButton("Test2"), BorderLayout.CENTER);
        add(new JButton("Test3"), BorderLayout.EAST);
    }
}

基本上我希望能够 expand/show/resize 滚动窗格中的每个面板独立于其他面板。按照目前的情况,如果我在一个面板上显示一个隐藏的面板,另一个面板的高度会增长以匹配但不显示该组件。对我来说很奇怪,但可能是我对某些 Swing 组件及其包含的约束的无知。

Is there some sort of constraint in the JScrollPane that resizes it's child JPanel's components to retain the same height at all times?

滚动窗格不会调整任何内容。它只显示添加到滚动窗格的组件,并在添加的组件的首选大小大于滚动窗格的大小时添加滚动条。

wrapPanel.setLayout(new GridLayout(numUnits, 1));  

另一方面,当您使用 GridLayout 时,是的,所有添加到网格的组件都将调整为最大组件的大小。

所以您不想为包装面板使用 GridLayout。

我建议您可以使用 GridBagLayout 或 BoxLayout。作为面板。

那么我建议您使用 BorderLayout 作为可扩展面板。您将始终对 CENTER 可见的部分和可扩展部分添加到 PAGE_END。然后,当您想让面板展开时,只需更改 PAGE_END.

中组件的可见性即可

然后布局管理器将完成所有工作,重新计算所有面板的适当大小。