Search code examples
javaswinglayoutjbuttoncontainers

Making a Java SplitButton


The following code seems like it should fill the given space evenly with two associated JButtons, but instead it fills the FlowLayout's entire top row with one button--ignoring the button's maximum size--and puts the other button on a new row, only partially visible. How can I get the buttons to evenly fill the JPanel side-by-side?

import java.awt.*;
import javax.swing.*;

public class SplitButton extends JPanel {

    // The other half.
    private JButton button2;
    private JButton button3;

    public SplitButton(String left, String right, int width, int height) {
        super();
        //setBackground(Color.GREEN);
        //setBorder(BorderFactory.createLineBorder(Color.BLUE));
        setMinimumSize(new Dimension(width, height));
        FlowLayout flow = new FlowLayout();
        flow.setAlignment(FlowLayout.LEFT);
        flow.setHgap(0);
        flow.setVgap(0);

        setLayout(flow);

        button2 = new JButton(right);
        button2.setMinimumSize(new Dimension(width / 2, height));
        button2.setMaximumSize(new Dimension(width / 2, height));
        button2.setPreferredSize(new Dimension(width / 2, height));
        add(button2);

        button3 = new JButton(right);
        button3.setMinimumSize(new Dimension(width / 2, height));
        button2.setMaximumSize(new Dimension(width / 2, height));
        button2.setPreferredSize(new Dimension(width / 2, height));
        add(button3);
    }
}

Solution

  • After a while of struggling, I tried a GridLayout--which makes SplitButtons in Swing a very rational UI element.

    import java.awt.Font;
    import java.awt.Color;
    import java.awt.GridLayout;
    import java.awt.event.ActionListener;
    import javax.swing.BorderFactory;
    import javax.swing.JButton;
    import javax.swing.JPanel;
    
    public class SplitButton extends JPanel {
    
        // The buttons.
        private JButton[] buttons;
    
        // Self-explanatory.
        public Color prim, trim;
        public Font font;
    
        public SplitButton(String[] labels, Color prim, Color trim, Font font) {
            super();
    
            this.prim = prim;
            this.trim = trim;
            this.font = font;
    
            // Evenly lays out buttons evenly in a row.
            GridLayout grid = new GridLayout(1, labels.length);
            grid.setHgap(0);
            grid.setVgap(0);
            setLayout(grid);
    
            setOpaque(false);
            initButtons(labels);
        }
    
        /**
        * Constructs and styles the button array.
        * @param labels The button labels.
        */
        private void initButtons(String[] labels) {
            buttons = new JButton[labels.length];
            for(int i = 0; i < buttons.length; i++) {
                buttons[i] = new JButton(labels[i]);
    
                // Only outer borders are thick.
                int lb = (i == 0) ? 2 : 1;
                int rb = (i == buttons.length - 1) ? 2 : 0;
    
                buttons[i].setBorder(BorderFactory.createMatteBorder(2, lb, 2, rb, trim));
                buttons[i].setFocusPainted(false);
                buttons[i].setFont(font);
                buttons[i].setBackground(prim);
                buttons[i].setForeground(trim);
                add(buttons[i]);
            }
        }
    
        /**
        * Enable/disable the buttons.
        * @param enabled Is the button enabled?
        */
        public void setEnabled(boolean enabled) {
            if(buttons != null)
                for(int i = 0; i < buttons.length; i++)
                    buttons[i].setEnabled(enabled);
        }
    
        /**
        * Listen to all the buttons.
        * @param listener The listening object.
        */
        public void addActionListener(ActionListener listener) {
            if(buttons != null)
                for(int i = 0; i < buttons.length; i++)
                    buttons[i].addActionListener(listener);
        }
    }