【问题标题】:How can I resolve these sizing issues with JToggleButtons, JPanels, and JScrollPanes?如何使用 JToggleButtons、JPanels 和 JScrollPanes 解决这些大小调整问题?
【发布时间】:2015-06-29 03:05:00
【问题描述】:

我正在尝试开发一种手风琴菜单形式。有少数 (2-12) 个选项可以打开/关闭。打开时,将有一个JPanel,其中包含可见的其他设置。关闭后,其他设置将不可见。

我创建了一个 SelectableExpandablePanel 类,它扩展了 JPanel 并实现了 ActionListenerComponentListener。该面板包含两个东西 - 一个JToggleButton 和一个孩子Component(通常是一个JPanel,但我不想限制自己以供将来重用这个概念)在BoxLayout 中强制执行一列。选择切换按钮时,子项变为可见。取消选择切换时,子项被隐藏。

当我使用这个组件时,我打算把它放在JScrollPane 内部的JPanel 上,如示例 main 方法中所示。

似乎有两个问题我难以克服:

    1234563当我点击按钮时,我希望JScrollPane 会做它的事情并生成一个垂直滚动条。这不会发生。
  1. 我希望切换按钮是包含它们的 JPanel 的整个宽度。我以为我在构造函数中所做的加上Component Listener 会处理这个问题,但事实并非如此。

下面提供的内容是编译并有一个 main 方法。如果编译并执行,它会驱动我正在构建的组件以提供测试框架和重现我正在谈论的问题的能力。

import javax.swing.BoxLayout;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToggleButton;

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;

public class SelectableExpandablePanel extends JPanel implements
        ActionListener, ComponentListener {
    private JToggleButton titleButton;
    private JComponent childComponent;

    public SelectableExpandablePanel(JComponent child) {
        this(child, null, null);
    }

    public SelectableExpandablePanel(JComponent child, String title) {
        this(child, title, null);
    }

    public SelectableExpandablePanel(JComponent child, String title,
            String tooltip) {
        super();

        if (child == null) {
            throw new IllegalArgumentException("Child component cannot be null");
        }

        childComponent = child;

        titleButton = new JToggleButton();
        titleButton.setText(title);
        titleButton.addActionListener(this);
        titleButton.setPreferredSize(new Dimension(getSize().width, titleButton
                .getPreferredSize().height));
        titleButton.setToolTipText(tooltip);

        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
        setPreferredSize(new Dimension(childComponent.getPreferredSize().width,
                titleButton.getPreferredSize().height));
        setSize(new Dimension(childComponent.getPreferredSize().width,
                titleButton.getPreferredSize().height));

        add(titleButton);

        this.addComponentListener(this);
    }

    public void actionPerformed(ActionEvent e) {
        if (titleButton.isSelected()) {
            add(childComponent);
            setSize(new Dimension(childComponent.getPreferredSize().width,
                    titleButton.getPreferredSize().height
                            + childComponent.getPreferredSize().height));

        } else {
            remove(childComponent);
            setSize(new Dimension(childComponent.getPreferredSize().width,
                    titleButton.getPreferredSize().height));
        }

        invalidate();
        revalidate();
    }

    public void componentHidden(ComponentEvent arg0) {
        // Do nothing
    }

    public void componentMoved(ComponentEvent arg0) {
        // Do nothing
    }

    public void componentResized(ComponentEvent arg0) {
        titleButton.setSize(this.getWidth(),
                titleButton.getPreferredSize().height);
    }

    public void componentShown(ComponentEvent arg0) {
        // Do nothing
    }

    public static void main(String[] args) {
        JScrollPane scrollPane = new JScrollPane();

        // These panels simulates a complex, multi-line configuration panel.
        JPanel testPanel = new JPanel();
        testPanel.setLayout(new BoxLayout(testPanel, BoxLayout.Y_AXIS));
        testPanel.add(new JLabel("Test JLabel"));
        testPanel.add(new JLabel("Test JLabel 2"));
        testPanel.add(new JLabel("Test JLabel 3"));

        JPanel testPanel2 = new JPanel();
        testPanel2.setLayout(new BoxLayout(testPanel2, BoxLayout.Y_AXIS));
        testPanel2.add(new JLabel("Test JLabel"));
        testPanel2.add(new JLabel("Test JLabel 2"));
        testPanel2.add(new JLabel("Test JLabel 3"));

        JPanel testPanel3 = new JPanel();
        testPanel3.setLayout(new BoxLayout(testPanel3, BoxLayout.Y_AXIS));
        testPanel3.add(new JLabel("Test JLabel"));
        testPanel3.add(new JLabel("Test JLabel 2"));
        testPanel3.add(new JLabel("Test JLabel 3"));

        // This panel simulates the panel that will contain each of the
        // SelectableExpandablePanels.
        JPanel testHolder = new JPanel();
        testHolder.setLayout(new BoxLayout(testHolder, BoxLayout.Y_AXIS));
        testHolder.add(new SelectableExpandablePanel(testPanel, "Test"));
        testHolder.add(new SelectableExpandablePanel(testPanel2, "Test 2"));
        testHolder.add(new SelectableExpandablePanel(testPanel3, "Test 3"));

        // We add the test holder to the scroll pane. The intention is that if
        // the expansion is too big to fit, the holding JFrame won't expand, but
        // the scroll pane will get scroll bars to let the user scroll up and
        // down through the toggle buttons and any enabled items.
        scrollPane.setViewportView(testHolder);

        JFrame testFrame = new JFrame("Expandable Panel Test");
        testFrame.getContentPane().add(scrollPane);
        testFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        testFrame.pack();
        testFrame.setVisible(true);
    }
}

【问题讨论】:

  • @mKorbel 让我看看那些。快速查看,看起来使用 setVisible 而不是添加或删除可能是一个更好的选择。我需要深入挖掘滚动窗格问题和宽度问题。
  • 使用 setVisible 而不是添加或删除可能会更好 ---> 在 JScrollPane 中无关紧要,是关于我的问题
  • @mKorbel 我不知道你在问什么。子面板可以调整大小,是的。
  • 一切都取决于什么、如何、何时(您期望什么)JFrames 子项(如果是或不是)可调整大小,那么答案会更容易

标签: java swing jpanel jscrollpane preferredsize


【解决方案1】:
  1. 删除所有的 setSize/setPreferredSize 调用,让 LayoutManager 完成它的工作。
  2. 要允许 JButtons 填充面板的宽度,您可以使用 BorderLayout(例如,将按钮添加到 CENTER,将子容器添加到 SOUTH 并删除所有这些 setSize 值以让 LayoutManager 处理它)。

【讨论】:

  • 这是完美的。我想。我需要进行更多测试,但这看起来像是我需要的功能。
  • 不要使用 setPreferredSize(),这是布局管理器的工作。
  • 唯一的问题是,如果我删除 setSize/setPreferredSize,SelectableExpandablePanel 的大小只是按钮的宽度。宽度需要是最宽子组件的宽度,因为我想防止水平滚动。除此之外,一切都运行良好。
  • 同意关于 setPreferredSize() 的通知,但另一种情况是 JScrollpane(他的回答中没有提到)
【解决方案2】:

不要试图自己管理尺寸:

//titleButton.setPreferredSize(new Dimension(getSize().width, titleButton.getPreferredSize().height));
 titleButton.setToolTipText(tooltip);

 setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
//setPreferredSize(new Dimension(childComponent.getPreferredSize().width, titleButton.getPreferredSize().height));
//setSize(new Dimension(childComponent.getPreferredSize().width, titleButton.getPreferredSize().height));

另外,去掉 ActionListener 中的setSize() 代码。无论如何,这将被忽略,因为布局管理器将确定大小。

当面板的首选大小大于滚动窗格的大小时,将出现滚动条。如果您硬编码首选尺寸,那么您默认布局管理器的用途,并且首选尺寸不会随着您添加/删除组件而改变。

注意这样的事情,我通常使用 BorderLayout。将按钮放在 PAGE_START 中,将另一个面板放在 CENTER 中。组件将自动填充可用空间。

【讨论】:

  • 您能否解决调整滚动窗格大小的首选方法?目前,它在我的主要方法中。不过,我想我在使用这个组件时会遇到这个问题。如果我像在示例 main 方法中那样将其放入滚动窗格中,则滚动窗格将是按钮的宽度,在某些情况下比子组件窄得多。我需要强制滚动窗格为最宽子组件的宽度。处理这个问题的最佳方法是什么?
  • @ThomasOwens,如果您需要管理面板的首选大小,请覆盖类的 getPreferredSize() 方法。这样,每次调用布局管理器时都会动态计算首选大小。
猜你喜欢
  • 1970-01-01
  • 2021-06-25
  • 2010-12-22
  • 1970-01-01
  • 2019-12-04
  • 2012-03-26
  • 1970-01-01
  • 1970-01-01
  • 2012-05-07
相关资源
最近更新 更多