javaswingborder-layoutjseparator

Unable to vertically align horizontal JSeparator in BorderLayout


Using BorderLayout I have put two different JButtons, one on the left (west) and one on the right (east), and a horizontal JSeparator in the center. What I want to do is to y-align the separator to the center, instead of to the top as it is now. I have already tried to use the following method on the separator

setAlignmentY(CENTER_ALIGNMENT);

but it has absolutely no effect. What am I missing? If it is not possible, is there any other way to do that without using external libraries?

This is what I get:

Image of the problem

and this is what I want to achieve:

Image of the solution

This is the sample code that I am using (JPanels on top and bottom were added just for clarity):

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

public class SeparatorTest extends JFrame{

    JButton btn1 = new JButton("button1");
    JSeparator sep = new JSeparator(SwingConstants.HORIZONTAL);
    JButton btn2 = new JButton("button2");

    public SeparatorTest() {
        getContentPane().add(BorderLayout.NORTH, new JPanel());
        getContentPane().add(BorderLayout.WEST, btn1);
        getContentPane().add(BorderLayout.CENTER, sep);
        getContentPane().add(BorderLayout.EAST, btn2);
        getContentPane().add(BorderLayout.SOUTH, new JPanel());

        setSize(300, 85);
    }

    public static void main(String[] args){
        new SeparatorTest().setVisible(true);
    }
}

EDIT 1: I don't mind the layout as long as it looks the same, I used BorderLayout here due to its simpleness.


Solution

  • This will have to do with how the JSeparator UI delegate decides how to paint the component, which, based on your tests, seems to want to always paint the separator starting at a y position of 0.

    Instead, you might need to wrap the JSeparator in another panel which can use a different layout manager which meets your needs, like GridBagLayout for example (of course you could just use GridBagLayout to start with, but I'm aiming for the smallest change possible ;))

    buttons

    import java.awt.BorderLayout;
    import java.awt.EventQueue;
    import java.awt.GridBagConstraints;
    import java.awt.GridBagLayout;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JSeparator;
    import javax.swing.SwingConstants;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class SeparatorTest extends JFrame {
    
        JButton btn1 = new JButton("button1");
        JSeparator sep = new JSeparator(SwingConstants.HORIZONTAL);
        JButton btn2 = new JButton("button2");
    
        public SeparatorTest() {
    
            JPanel pane = new JPanel(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridwidth = GridBagConstraints.REMAINDER;
            gbc.weightx = 1;
            gbc.fill = GridBagConstraints.HORIZONTAL;
            pane.add(sep, gbc);
    
            getContentPane().add(BorderLayout.NORTH, new JPanel());
            getContentPane().add(BorderLayout.WEST, btn1);
            getContentPane().add(BorderLayout.CENTER, pane);
            getContentPane().add(BorderLayout.EAST, btn2);
            getContentPane().add(BorderLayout.SOUTH, new JPanel());
    
            setSize(300, 85);
        }
    
        public static void main(String[] args) {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    }
    
                    SeparatorTest frame = new SeparatorTest();
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    }