Java 如何将JSpinner的边框更改为圆角半径可调的自定义彩色边框i

Java 如何将JSpinner的边框更改为圆角半径可调的自定义彩色边框i,java,swing,jspinner,Java,Swing,Jspinner,我想定制我的JSpinner,给它一个定制的边框,颜色可调,边框厚度可调,圆角半径可调。这样我就可以为微调器设置边界并完成它 我的微调器代码如下所示: protected JSpinner createLabelledUpDownControl(JComponent parent, int initialValue, int minVal, int maxVal, String topLabelString, Font topLabelFont, Rectangle topLabelBounds

我想定制我的JSpinner,给它一个定制的边框,颜色可调,边框厚度可调,圆角半径可调。这样我就可以为微调器设置边界并完成它

我的微调器代码如下所示:

protected JSpinner createLabelledUpDownControl(JComponent parent, int initialValue, int minVal, int maxVal, String topLabelString, Font topLabelFont, Rectangle topLabelBounds, String topSubLabelString, Font topSubLabelFont, Rectangle topSubLabelBounds,String eachLabelString, Font eachLabelFont, Rectangle eachLabelBounds, String bottomLabelString, Font bottomLabelFont, Rectangle bottomLabelBounds ){
        @SuppressWarnings("serial")
        JSpinner spinner = new JSpinner(new SpinnerNumberModel(initialValue, minVal, maxVal, 1)){
            @Override
            public void paint(Graphics g){
                super.paint(g);
                Graphics2D g2D = (Graphics2D) g.create();
                RenderingHints qualityHints =  new RenderingHints(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
                qualityHints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY );
                g2D.setRenderingHints(qualityHints);  
            }
        };
        //spinner.setBorder(BorderFactory.createLineBorder(new Color(37, 54, 142), 4, true));

        //spinner.setBorder(new RoundedColouredBorder(30, new Color(37, 54, 142), 4));

        spinner.setBorder(new RoundedBorder(30, new Color(37, 54, 142), 4));

        spinner.setBounds(0, 0, parent.getWidth(), parent.getHeight());
        spinner.setFont(UI.getRegularArgentumSansFont().deriveFont(Font.BOLD, 88));
        spinner.setUI(new JSpinnerArrow(parent));


        JSpinner.DefaultEditor spinnerEditor = (JSpinner.DefaultEditor)spinner.getEditor();
        spinnerEditor.getTextField().setHorizontalAlignment(JTextField.CENTER);

        JComponent comp = spinner.getEditor();
        JFormattedTextField field = (JFormattedTextField) comp.getComponent(0);
        DefaultFormatter formatter = (DefaultFormatter) field.getFormatter();
        formatter.setCommitsOnValidEdit(true);

    if(parent != null){
            parent.add(spinner);
        }

        return spinner;
    }
我为微调器提供了以下类的自定义箭头:

我设置了箭头的尺寸,以便它们的大小根据我的需要而变化。我认为这一切都非常简单明了。但是,当我尝试为箭头按钮提供自定义边框时,以及当我尝试为整个微调器提供自定义边框时,就会出现问题

private class JSpinnerArrow extends BasicSpinnerUI {

        private JComponent parent;

        public JSpinnerArrow(JComponent parent){
            this.parent = parent;
        }

        @Override
        protected Component createNextButton() {
            Component c = createArrowButton("/arrow-upDB.png");
            c.setName("Spinner.nextButton");
            installNextButtonListeners(c);
            return c;
        }

        @Override
        public void paint(Graphics g, JComponent component){
            super.paint(g, component);
            Graphics2D g2D = (Graphics2D) g.create();
            RenderingHints qualityHints =  new RenderingHints(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
            qualityHints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY );
            g2D.setRenderingHints(qualityHints);  
        }

        @Override
        protected Component createPreviousButton() {
            Component c = createArrowButton("/arrow-downDB.png");
            c.setName("Spinner.previousButton");
            installPreviousButtonListeners(c);
            return c;
        }

        private Component createArrowButton(String filename) {
            Image icon = UI.loadImage(filename);
            if(icon != null){
                JButton b = createButton(null, "", "", null);
                b.setIcon(new ImageIcon(icon));
                //b.setBorder(BorderFactory.createLineBorder(new Color(37, 54, 142), 4));
                b.setBackground(null);
                b.setBorder(new RoundedBorder(30, new Color(37, 54, 142), 4));
                b.setPreferredSize(new Dimension(65,160));
                return b;
            }
            return createButton(null, "", "", null);
        }
     }
我尝试了以下结果:请注意微调器文本区域是如何向内剪裁的(我相信它也被奇怪地拉伸了…并且边框没有绘制在微调器的最右侧边缘上)。 类的微调器结果:RoundedBorder

我还尝试了以下方法来绘制边框:这得到了以下结果:来自类的微调器结果:roundedcoloredborder

这一次,由于某些原因,边框不干净,微调器文本区域会剪辑到微调器边框中,使其具有奇怪的外圆边,但具有内锐角边(不是我想要的),并且再次在微调器的右侧绘制边框

public static class RoundedColouredBorder implements Border {
        private int radius;
        private int thickness;
        private Color color;

        public RoundedColouredBorder(int radius, Color borderColor, int thickness) {
            this.radius = radius;
            this.color = borderColor;
            this.thickness = thickness;
        }

        public Insets getBorderInsets(Component c) {
            return new Insets(this.thickness+1, this.thickness+1, this.thickness+2, this.thickness);
        }

        public boolean isBorderOpaque() {
            return true;
        }

        public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
            Dimension arcs = new Dimension(radius, radius);

            Graphics2D graphics = (Graphics2D) g;
            graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

            //Draws the rounded panel with borders.
            graphics.setColor(color);
            graphics.fillRoundRect(0, 0, width + thickness, height + thickness, arcs.width, arcs.height); //paint background
            graphics.drawRoundRect(0, 0, width - thickness, height  -thickness, arcs.width, arcs.height); //paint border

        }
    }
我想画的是:

期望结果

因此,基本上为了达到我想要的效果,我希望在整个微调器周围有一个圆形边框,在每个箭头按钮周围有一个圆形边框,我可以为其调整颜色、边框的厚度和角的半径


在我以上使用RoundedBorder和RoundedColouredBorder类的两次尝试中,我得到了非常奇怪的剪裁,而且边框没有我想要的那么干净。从RoundedBorder类获得的结果似乎将白色微调器文本区域切割为更小的大小,并以一种非常奇怪的方式重新蚀刻。我做错了什么?

之后这篇文章的中间部分是我的答案代码,但我想先与大家分享一下找出答案的努力,尝试并让大家相信这是我(至少)能找到的最好的解决方案。下面是:

  • 您需要在按钮和微调器周围绘制一个弧半径可调的边框。因此,您需要定义自己的边框,因为没有其他边框可以这样做。实际上,这是我能找到的最接近的边框,因为有一个构造参数roundedCorners,它使用一个不可调的值圆角边框角的弧半径。a在研究了
    LineBorder
    的实现之后,我认为您可以安全地将其子类化为始终具有圆角以及可调整的圆弧半径。因此,您只需覆盖
    paintBorder
    以始终绘制圆角,然后为圆弧半径制作一个setter和getter
  • 你需要一个自定义形状的按钮(例如,根据你的问题)。这意味着它不仅将被绘制为自定义形状,而且自定义形状还将用于定义鼠标光标是否位于按钮上方。正如我发现的(这意味着我可能错了,但这是我最好的尝试)第二部分有两个选项:

  • 覆盖以定义按钮内的点。这意味着子类化
    组件UI
    (或更适当地子类化以能够设置按钮的UI)
  • 覆盖以定义按钮内的点。这意味着子类化(或者更恰当地说,在本例中是子类化)
  • 您可能想知道这两个选项中的哪一个实际上用于定义按钮内部的点。好吧,两者都是,因为
    ComponentUI.contains的默认实现包含
    委托给
    Component.contains
    。尽管如此,第二个选项看起来已经更好了,因为它看起来更像是PLAF独立的。但是,对于第一个选项艺术,您还需要只在您定义的形状内绘制按钮,而不在其(方形)边界内。这意味着覆盖和更改按钮(这意味着子类化,或者更合适地设置自定义剪辑)。因此,这将导致我们将子类化
    JButton
    ,并立即解决这两个问题(可能单独加上PLAF)

  • 您需要一个自定义形状的微调器。根据我们需要对
    JButton
    类进行子类化的原因,我们还需要对该类进行子类化以提供自定义形状
  • 我还注意到,在您想要的结果中,两个按钮之间有一个间隙。通过搜索
    JSpinner
    的实现及其UI,我还知道有3个组件被添加到
    JSpinner
    (因为这是一个正常情况):编辑器、下一个按钮和上一个按钮。那么谁负责设置添加到容器中的组件的位置和大小?…其。因此,您还需要一个自定义的
    LayoutManager
    ,它将在微调器中布局按钮时添加按钮之间的间隙。
    layoutmanag的当前实现JSpinner的er
    可以作为
    处理程序
    类在内部找到。我只是想让您知道,如果您想用自己的自定义
    布局管理器
    扩展它的操作。在本文的代码中,我还基于
    处理程序
    类实现了一个自定义
    布局管理器
  • 在进一步搜索了
    JSpinner
    的实现及其UI之后,我发现spinner的3个组件创建如下:

    protected JSpinner createLabelledUpDownControl(JComponent parent, int initialValue, int minVal, int maxVal, String topLabelString, Font topLabelFont, Rectangle topLabelBounds, String topSubLabelString, Font topSubLabelFont, Rectangle topSubLabelBounds,String eachLabelString, Font eachLabelFont, Rectangle eachLabelBounds, String bottomLabelString, Font bottomLabelFont, Rectangle bottomLabelBounds ){
            @SuppressWarnings("serial")
            JSpinner spinner = new JSpinner(new SpinnerNumberModel(initialValue, minVal, maxVal, 1)){
                @Override
                public void paint(Graphics g){
                    super.paint(g);
                    Graphics2D g2D = (Graphics2D) g.create();
                    RenderingHints qualityHints =  new RenderingHints(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
                    qualityHints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY );
                    g2D.setRenderingHints(qualityHints);  
                }
            };
            //spinner.setBorder(BorderFactory.createLineBorder(new Color(37, 54, 142), 4, true));
    
            //spinner.setBorder(new RoundedColouredBorder(30, new Color(37, 54, 142), 4));
    
            spinner.setBorder(new RoundedBorder(30, new Color(37, 54, 142), 4));
    
            spinner.setBounds(0, 0, parent.getWidth(), parent.getHeight());
            spinner.setFont(UI.getRegularArgentumSansFont().deriveFont(Font.BOLD, 88));
            spinner.setUI(new JSpinnerArrow(parent));
    
    
            JSpinner.DefaultEditor spinnerEditor = (JSpinner.DefaultEditor)spinner.getEditor();
            spinnerEditor.getTextField().setHorizontalAlignment(JTextField.CENTER);
    
            JComponent comp = spinner.getEditor();
            JFormattedTextField field = (JFormattedTextField) comp.getComponent(0);
            DefaultFormatter formatter = (DefaultFormatter) field.getFormatter();
            formatter.setCommitsOnValidEdit(true);
    
        if(parent != null){
                parent.add(spinner);
            }
    
            return spinner;
        }
    
  • 编辑器是在
    JSpinner
    本身内部创建的,具体取决于。然后微调器的UI获取(使用)微调器的编辑器并对其进行初始化
  • “下一步”按钮实际上是在微调器的UI(
    BasicSpinnerUI)中创建的<
    
    import java.awt.Color;
    import java.awt.Component;
    import java.awt.Container;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Insets;
    import java.awt.LayoutManager;
    import java.awt.Shape;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import java.awt.geom.Path2D;
    import java.awt.geom.RoundRectangle2D;
    import javax.swing.JButton;
    import javax.swing.JComponent;
    import javax.swing.JFormattedTextField;
    import javax.swing.JFrame;
    import javax.swing.JSpinner;
    import javax.swing.JSpinner.DefaultEditor;
    import javax.swing.JTextField;
    import javax.swing.SpinnerModel;
    import javax.swing.SpinnerNumberModel;
    import javax.swing.border.LineBorder;
    import javax.swing.plaf.basic.BasicSpinnerUI;
    
    public class Main {
    
        //This is a LineBorder only that it always paints a RoundRectangle2Ds instead of Rectangle2Ds.
        public static class CustomLineBorder extends LineBorder {
            private double arcw, arch;
    
            public CustomLineBorder(Color color, int thickness, double arcw, double arch) {
                super(color, thickness);
                this.arcw = arcw;
                this.arch = arch;
            }
    
            //Note: the implementation of this paintBorder is inspired by the superclass.
            @Override
            public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
                if ((thickness > 0) && (g instanceof Graphics2D)) {
                    Graphics2D g2d = (Graphics2D) g;
                    Color oldColor = g2d.getColor();
                    g2d.setColor(lineColor);
                    Path2D path = new Path2D.Double(Path2D.WIND_EVEN_ODD);
                    path.append(new RoundRectangle2D.Double(x, y, width, height, thickness, thickness), false);
                    path.append(new RoundRectangle2D.Double(x + thickness, y + thickness, width - 2 * thickness, height - 2 * thickness, arcw, arch), false);
                    g2d.fill(path);
                    g2d.setColor(oldColor);
                }
            }
    
            public void setArcWidth(double arcw) {
                this.arcw = arcw;
            }
    
            public void setArcHeight(double arch) {
                this.arch = arch;
            }
    
            public void setLineColor(Color lineColor) {
                this.lineColor = lineColor;
            }
    
            public double getArcWidth() {
                return arcw;
            }
    
            public double getArcHeight() {
                return arch;
            }
        }
    
        public static class CustomJButton extends JButton {
            private double arcw, arch;
    
            public CustomJButton(double arcw, double arch) {
                this.arcw = arcw;
                this.arch = arch;
            }
    
            public void setArcWidth(double arcw) {
                this.arcw = arcw;
                revalidate(); //Not sure if needed.
                repaint();
            }
    
            public void setArcHeight(double arch) {
                this.arch = arch;
                revalidate(); //Not sure if needed.
                repaint();
            }
    
            public double getArcWidth() {
                return arcw;
            }
    
            public double getArcHeight() {
                return arch;
            }
    
            @Override
            public Dimension getPreferredSize() {
                //Here you set the preferred size of the button to something which takes into account the arc width and height:
                Dimension sz = super.getPreferredSize();
                sz.width = Math.max(sz.width, Math.round((float) getArcWidth()));
                sz.height = Math.max(sz.height, Math.round((float) getArcHeight()));
                return sz;
            }
    
            //Note that the width/height/arcw/arch of the component are not constant. Thats why we create a new instance of RoundRectangle2D.Double every time...
            protected Shape createShape() {
                return new RoundRectangle2D.Double(0, 0, getWidth(), getHeight(), getArcWidth(), getArcHeight());
            }
    
            //Paint only inside the createShape's Shape:
            @Override
            public void paint(Graphics g) {
                Graphics2D g2d = (Graphics2D) g.create();
                g2d.setClip(createShape());
                super.paint(g2d);
                g2d.dispose();
            }
    
            //Update only inside the createShape's Shape:
            @Override
            public void update(Graphics g) {
                Graphics2D g2d = (Graphics2D) g.create();
                g2d.setClip(createShape());
                super.update(g2d);
                g2d.dispose();
            }
    
            //Tell which points are inside this button:
            @Override
            public boolean contains(int x, int y) {
                return createShape().contains(x, y);
            }
        }
    
        //The implementation of this subclass is inspired by the private static class Handler of the BasicSpinnerUI:
        public static class CustomJSpinnerLayout implements LayoutManager {
            private final int gap; //You can make this non-final and add setter and getter, but remember
            //to call revalidate() on the spinner whenever you change this gap of this class...
    
            private Component nextButton;
            private Component previousButton;
            private Component editor;
    
            public CustomJSpinnerLayout(int gap) {
                this.gap = gap;
                nextButton = null;
                previousButton = null;
                editor = null;
            }
    
            //Only recognizes 3 components ("Next", "Previous" and "Editor"). Others are not layed out.
            @Override
            public void addLayoutComponent(String constraints, Component c) {
                switch (constraints) {
                    case "Next": nextButton = c; break;
                    case "Previous": previousButton = c; break;
                    case "Editor": editor = c; break;
                }
            }
    
            @Override
            public void removeLayoutComponent(Component c) {
                if (c == nextButton)
                    nextButton = null;
                else if (c == previousButton)
                    previousButton = null;
                else if (c == editor)
                    editor = null;
            }
    
            @Override
            public Dimension preferredLayoutSize(Container parent) {
                return minimumLayoutSize(parent);
            }
    
            //Only recognizes 3 components ("Next", "Previous" and "Editor"). Others are not taken into account.
            @Override
            public Dimension minimumLayoutSize(Container parent) {
                Dimension next = nextButton.getPreferredSize();
                Dimension prev = previousButton.getPreferredSize();
                Dimension edit = editor.getPreferredSize();
                Insets pari = parent.getInsets();
                int totalHeight = Math.max(edit.height, next.height + prev.height + gap);
                int buttonMaxWidth = Math.max(next.width, prev.width);
                return new Dimension(buttonMaxWidth + edit.width + pari.left, totalHeight + pari.top + pari.bottom);
            }
    
            //Only recognizes 3 components ("Next", "Previous" and "Editor"). Others are not layed out.
            @Override
            public void layoutContainer(Container parent) {
                if (editor != null || nextButton != null || previousButton != null) {
                    //Warning: does not account for component orientation (eg leftToRight or not).
                    Dimension prnt = parent.getSize();
                    Dimension next = nextButton.getPreferredSize();
                    Dimension prev = previousButton.getPreferredSize();
                    Insets i = parent.getInsets();
                    int maxButtonWidth = Math.max(next.width, prev.width);
                    int buttonHeight = Math.round((prnt.height - gap) / 2f);
                    editor.setBounds(i.left, i.top, prnt.width - i.left - i.right - maxButtonWidth, prnt.height - i.top - i.bottom);
                    nextButton.setBounds(prnt.width - maxButtonWidth, 0, maxButtonWidth, buttonHeight);
                    previousButton.setBounds(prnt.width - maxButtonWidth, prnt.height - buttonHeight, maxButtonWidth, buttonHeight);
                }
            }
        }
    
        public static class CustomBasicSpinnerUI extends BasicSpinnerUI {
    
            //Works like createEditor() of BasicSpinnerUI, in that it gets the spinner's button from the spinner itself.
            @Override
            protected Component createPreviousButton() {
                if (spinner instanceof CustomJSpinner) {
                    CustomJButton prev = ((CustomJSpinner) spinner).getButtonPrevious();
                    prev.setInheritsPopupMenu(true); //Inspired by the code of the private BasicSpinnerUI.createArrowButton().
                    prev.setName("Spinner.previousButton"); //Required by the code of BasicSpinnerUI.createPreviousButton().
                    installPreviousButtonListeners(prev); //Required by the code of BasicSpinnerUI.createPreviousButton().
                    return prev;
                }
                return super.createPreviousButton(); //If this UI is added to a non CustomJSpinner, then return default implementation.
            }
    
            //Works like createEditor() of BasicSpinnerUI, in that it gets the spinner's button from the spinner itself.
            @Override
            protected Component createNextButton() {
                if (spinner instanceof CustomJSpinner) {
                    CustomJButton next = ((CustomJSpinner) spinner).getButtonNext();
                    next.setInheritsPopupMenu(true); //Inspired by the code of the private BasicSpinnerUI.createArrowButton().
                    next.setName("Spinner.nextButton"); //Required by the code of BasicSpinnerUI.createNextButton().
                    installNextButtonListeners(next); //Required by the code of BasicSpinnerUI.createNextButton().
                    return next;
                }
                return super.createNextButton(); //If this UI is added to a non CustomJSpinner, then return default implementation.
            }
    
            //Creates the default LayoutManager for the JSpinner.
            //Could be replaced by a call to setLayout on the custom JSpinner.
            @Override
            protected LayoutManager createLayout() {
                return new CustomJSpinnerLayout(8);
            }
        }
    
        public static class CustomJSpinner extends JSpinner {
            private CustomJButton next, prev; //Maintain a reference to the buttons, just like the JSpinner does for the editor...
            private double arcw, arch;
    
            public CustomJSpinner(SpinnerModel model, double arcw, double arch) {
                super(model);
                this.arcw = arcw;
                this.arch = arch;
                next = new CustomJButton(arcw, arch);
                prev = new CustomJButton(arcw, arch);
            }
    
            public void setButtonPrevious(CustomJButton prev) {
                this.prev = prev;
                revalidate();
                repaint();
            }
    
            public void setButtonNext(CustomJButton next) {
                this.next = next;
                revalidate();
                repaint();
            }
    
            public CustomJButton getButtonPrevious() {
                return prev;
            }
    
            public CustomJButton getButtonNext() {
                return next;
            }
    
            public void setArcWidth(double arcw) {
                this.arcw = arcw;
                revalidate(); //Not sure if needed.
                repaint();
            }
    
            public void setArcHeight(double arch) {
                this.arch = arch;
                revalidate(); //Not sure if needed.
                repaint();
            }
    
            public double getArcWidth() {
                return arcw;
            }
    
            public double getArcHeight() {
                return arch;
            }
    
            //Note that the width/height/arcw/arch of the component are not constant. Thats why we create a new instance of RoundRectangle2D.Double every time...
            protected Shape createShape() {
                return new RoundRectangle2D.Double(0, 0, getWidth(), getHeight(), arcw, arch);
            }
    
            //Paint only inside the createShape's Shape:
            @Override
            public void paint(Graphics g) {
                Graphics2D g2d = (Graphics2D) g.create();
                g2d.setClip(createShape());
                Color old = g2d.getColor();
                g2d.setColor(getBackground());
                g2d.fillRect(0, 0, getWidth(), getHeight());
                g2d.setColor(old);
                super.paint(g2d);
                g2d.dispose();
            }
    
            //Update only inside the createShape's Shape:
            @Override
            public void update(Graphics g) {
                Graphics2D g2d = (Graphics2D) g.create();
                g2d.setClip(createShape());
                Color old = g2d.getColor();
                g2d.setColor(getBackground());
                g2d.fillRect(0, 0, getWidth(), getHeight());
                g2d.setColor(old);
                super.update(g2d);
                g2d.dispose();
            }
    
            //Tell which points are inside this spinner:
            @Override
            public boolean contains(int x, int y) {
                return createShape().contains(x, y);
            }
        }
    
        private static void initCustomJButton(CustomJButton cjb, String text, Color nonRolloverBorderColor, Color rolloverBorderColor, int borderThickness) {
            cjb.setOpaque(false); //Mandatory.
            cjb.setText(text); //Could be setIcon...
    
            //All the folllowing steps of this method are optional (remove them, edit them, etc as you like).
    
            //Add a CustomLineBorder to the CustomJButton (upon your request):
            CustomLineBorder clb = new CustomLineBorder(nonRolloverBorderColor, borderThickness, cjb.getArcWidth(), cjb.getArcHeight());
            cjb.setBorder(clb);
    
            //Create the mouse rollover effect of changing the color of the border of the button when the mouse hovers over the button:
            cjb.addMouseListener(new MouseAdapter() {
                @Override
                public void mouseEntered(MouseEvent mevt) {
                    clb.setLineColor(rolloverBorderColor);
                    cjb.repaint();
                }
    
                @Override
                public void mouseExited(MouseEvent mevt) {
                    clb.setLineColor(nonRolloverBorderColor);
                    cjb.repaint();
                }
            });
        }
    
        public static void main(String[] args) {
    
            //Setup parameters:
            double arcw = 50, arch = 50;
            int borderThickness = 2;
            Color borderMainColor = Color.CYAN.darker(), buttonRolloverBorderColor = Color.CYAN;
    
            //Create the spinner:
            CustomJSpinner spin = new CustomJSpinner(new SpinnerNumberModel(), arcw, arch);
    
            //Customizing spinner:
            spin.setUI(new CustomBasicSpinnerUI()); //Mandatory first step!
            spin.setOpaque(false); //Mandatory.
            spin.setBorder(new CustomLineBorder(borderMainColor, borderThickness, spin.getArcWidth(), spin.getArcHeight())); //Upon your request.
            spin.setPreferredSize(new Dimension(200, 200)); //Optional.
            spin.setBackground(Color.RED); //Obviously needs to be changed to "Color.WHITE", but for demonstration let it be "Color.RED".
    
            //Customizing spinner's buttons:
            initCustomJButton(spin.getButtonNext(), "Next", borderMainColor, buttonRolloverBorderColor, borderThickness);
            initCustomJButton(spin.getButtonPrevious(), "Prev", borderMainColor, buttonRolloverBorderColor, borderThickness);
    
            //Customizing spinner's editor:
            JComponent editor = spin.getEditor();
            editor.setOpaque(false); //Mandatory.
            if (editor instanceof DefaultEditor) {
                JFormattedTextField jftf = ((DefaultEditor) editor).getTextField();
                jftf.setOpaque(false); //Mandatory.
                jftf.setHorizontalAlignment(JTextField.CENTER); //Upon your request.
                //jftf.setFont(new Font(Font.MONOSPACED, Font.ITALIC, 25));
            }
    
            JFrame frame = new JFrame("Customized JSpinner");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.getContentPane().add(spin);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
    }