javaswingjframejcomponentjspinner

How to invert JSpinner layout


I have a JSpinner:

Spinner

I want the up/down buttons to be on the left side, and also the text to align on the left.

Is there a simple way I can achieve this? Thank you

JSpinner spinner = new JSpinner();

Solution

  • You can override the methods found in BasicSpinnerUI for creating the next and previous buttons:

    Custom Spinner UI:

    import javax.swing.JButton;
    import javax.swing.JComponent;
    import javax.swing.SwingConstants;
    import javax.swing.UIManager;
    import javax.swing.border.Border;
    import javax.swing.border.CompoundBorder;
    import javax.swing.plaf.ComponentUI;
    import javax.swing.plaf.UIResource;
    import javax.swing.plaf.basic.BasicArrowButton;
    import javax.swing.plaf.basic.BasicSpinnerUI;
    import java.awt.Component;
    import java.awt.Dimension;
    import java.awt.BorderLayout;
    
    public class MyJSpinnerUI extends BasicSpinnerUI{
        public static ComponentUI createUI(JComponent c) {
            return new MyJSpinnerUI();
        }
    
        @Override 
        protected Component createNextButton(){
            // change arrow direction: up becomes right
            Component c = createArrowButton(SwingConstants.EAST);
            c.setName("Spinner.nextButton");
            c.setSize(new Dimension(5, 5));
            installNextButtonListeners(c);
            return c;
        }
    
        @Override
        protected Component createPreviousButton() {
            // change arrow direction: down becomes left
            Component c = createArrowButton(SwingConstants.WEST);
            c.setName("Spinner.previousButton");
            c.setSize(new Dimension(5, 5));
            installPreviousButtonListeners(c);
            return c;
        }
    
        @Override
        public void installUI(JComponent c) {
          super.installUI(c);
          c.removeAll();
          c.setLayout(new BorderLayout());
    
          // here you can play with their positioning
          c.add(createPreviousButton(), BorderLayout.WEST);
          c.add(createEditor(), BorderLayout.CENTER);
          c.add(createNextButton(), BorderLayout.EAST);
        }
    
        // private method inside BasicSpinnerUI
        private Component createArrowButton(int direction) {
            JButton b = new BasicArrowButton(direction);
            Border buttonBorder = UIManager.getBorder("Spinner.arrowButtonBorder");
            if (buttonBorder instanceof UIResource) {
                // Wrap the border to avoid having the UIResource be replaced by
                // the ButtonUI. This is the opposite of using BorderUIResource.
                b.setBorder(new CompoundBorder(buttonBorder, null));
            } else {
                b.setBorder(buttonBorder);
            }
            b.setInheritsPopupMenu(true);
            return b;
        }
    }
    

    Demo:

    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JSpinner;
    import java.awt.Dimension;
    import java.awt.FlowLayout;  
    
    public class JSpinnerTest{
    
        public static void main(String[] args) {
            JFrame frame = new JFrame("JSpinner changed arrows");  
            JPanel panel = new JPanel();  
            JSpinner jSpinner = new JSpinner();
    
            // apply your custom Spinner UI
            jSpinner.setUI(new MyJSpinnerUI());
    
            jSpinner.setPreferredSize(new Dimension(100, 40));
            panel.setLayout(new FlowLayout());  
            panel.add(jSpinner);  
            frame.add(panel);
            frame.setVisible(true);  
            frame.setSize(1000, 400);  
            frame.setLocationRelativeTo(null);  
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
    
        }
    }