I'm trying to develop a form of an accordion menu. There are a small number (2-12) options that can be toggled on/off. When toggled on, there will be a JPanel with additional settings that become visible. When toggled off, the additional settings will not be visible.
I've created a SelectableExpandablePanel class that extends JPanel and implements ActionListener and ComponentListener. The panel holds two things - a JToggleButton and a child Component (which will typically be a JPanel, but I don't want to limit myself for future reuse of this concept) in a BoxLayout to enforce one column. When the toggle button is selected, the child becomes visible. When the toggle is deselected, the child is hidden.
When I use this component, I intend to put it on a JPanel inside of a JScrollPane, as demonstrated in the sample main method.
There appear to be two problems that I'm having trouble overcoming:
If I don't specify a
JFramesize, it's only large enough for the width of each child and tall enough for three buttons. When I click on the button, I would expect theJScrollPaneto do its thing and generate a vertical scroll bar. This isn't happening.I'd like the toggle buttons to be the full width of the JPanel that contains them. I thought what I did in the constructor plus the
Component Listenerwould handle that, but it doesn't.
What is provided below compiles and has a main method. If compiled and executed, it drives the component I'm building to provide a test frame and the ability to reproduce the issues I'm talking about.
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);
    }
}