简体   繁体   中英

Java JButton set text background color

I have to create a rounded button with a precise color.

I did a lot of research in order to make it and I'm almost there!

I choose to use a rounded border because doing otherwise seem impossible to me:/ (I'm new to Java).

So I just need to find a way to set the background of the content of the button (the text) the right color and I'm done. (I currently have just the border and disabled the background in order to see the rounded part so the background of the text is empty...)

Result:

结果

Expected result:

预期结果

I've already tried theses:

package components;

import java.awt.Font;
import java.awt.Component;

import javax.swing.BorderFactory;
import javax.swing.JButton;

import utils.BrandColors;

public class Button extends JButton {

    private int xPadding = 10;

    public Button(String text) {
        super(text);
        this.init();
    }

    private void init() {
        this.setFont(new Font("Arial", Font.PLAIN, 16));
        this.setForeground(BrandColors.TEXT_ON_SECOUNDARY);

        this.setBorder(BorderFactory.createCompoundBorder(
            BorderFactory.createLineBorder(BrandColors.SECOUNDARY, 15, true),
            BorderFactory.createMatteBorder(0, this.xPadding, 0, this.xPadding, BrandColors.SECOUNDARY)
        ));

        // this.setBackground(BrandColors.SECOUNDARY);
        this.setOpaque(false);
    }

}

Thanks in advance for your responses:)

Border don't fill. So once you make your component transparent ( setOpaque(false) ) you'll lose the background color, but you'd have weird issue with the border drawing inside the painted background area of the component anyway.

There's no simple way to do this and in fact (for Swing) a "generally" better solution would be to do this at the look and feel level (where you'd gain ultimate control and could change ALL the buttons in the UI without ever changing the code they use, for example , example , example )

But I don't have the time to muck about with all that, so, instead, I'll go straight for the "custom painted, custom component" route instead.

在此处输入图像描述

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.RenderingHints;
import java.awt.geom.RoundRectangle2D;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() {
            setBorder(new EmptyBorder(32, 32, 32, 32));
            setLayout(new GridBagLayout());
            add(new Button("This is a test"));
        }

    }

    public class BrandColors {

        public static final Color TEXT_ON_SECOUNDARY = Color.WHITE;
        public static final Color SECOUNDARY = Color.RED;
    }

    public class Button extends JButton {

        private int xPadding = 10;

        public Button(String text) {
            super(text);
            this.init();
        }

        private void init() {
            this.setFont(new Font("Arial", Font.PLAIN, 16));
            this.setForeground(BrandColors.TEXT_ON_SECOUNDARY);

            this.setContentAreaFilled(false);
            this.setBorderPainted(false);

            this.setBackground(BrandColors.SECOUNDARY);
            this.setOpaque(false);
        }

        @Override
        protected void paintComponent(Graphics g) {
            Graphics2D g2d = (Graphics2D) g.create();
            RenderingHints hints = new RenderingHints(
                    RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON
            );
            g2d.setRenderingHints(hints);
            g2d.setColor(getBackground());
            g2d.fill(new RoundRectangle2D.Double(0, 0, getWidth() - 1, getHeight() - 1, 15, 15));
            g2d.setColor(getForeground());
            super.paintComponent(g2d);
            g2d.dispose();
        }

    }
}

Now, the trick here is in knowing that paintComponent will also render the text, so we need to paint the background BEFORE we call super.paintComponent

UI delegate example...

Now, one of the features of Swing is it's "pluggable look and feel". This allows you to modify the "look and feel" of components without having to modify the rest of the code.

The following example shows away to set a UI delegate for a specific instance of JButton

在此处输入图像描述

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.geom.RoundRectangle2D;
import javax.swing.AbstractButton;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.plaf.basic.BasicButtonUI;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() {
            setBorder(new EmptyBorder(32, 32, 32, 32));
            setLayout(new GridBagLayout());

            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridwidth = GridBagConstraints.REMAINDER;
            gbc.insets = new Insets(4, 4, 4, 4);

            JButton button = new JButton("This is a normal button");
            add(button, gbc);
            JButton superButton = new JButton("This is a super button");
            superButton.setUI(new RoundedButtonUI());
            add(superButton, gbc);
        }

    }

    public class BrandColors {
        public static final Color TEXT_ON_SECOUNDARY = Color.WHITE;
        public static final Color SECOUNDARY = Color.RED;
    }

    public class RoundedButtonUI extends BasicButtonUI {
        @Override
        protected void installDefaults(AbstractButton b) {
            super.installDefaults(b);
            b.setOpaque(false);
            b.setBackground(BrandColors.SECOUNDARY);
            b.setForeground(BrandColors.TEXT_ON_SECOUNDARY);
        }

        @Override
        public void paint(Graphics g, JComponent c) {
            Graphics2D g2d = (Graphics2D) g.create();
            RenderingHints hints = new RenderingHints(
                    RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON
            );
            g2d.setRenderingHints(hints);
            g2d.setColor(c.getBackground());
            g2d.fill(new RoundRectangle2D.Double(0, 0, c.getWidth() - 1, c.getHeight() - 1, 15, 15));
            g2d.setColor(c.getForeground());
            super.paint(g, c);
            g2d.dispose();
        }

    }
}

Effect ALL buttons in the UI

If you want to change ALL the buttons in the UI, without having to change any of the related code, you can set the UI delegate as the default UI delegate to be used by all buttons

To do this, I had to make a couple of additional changes. First, the delegate class needs to be in it's own file (please take note of the package name) and I had to implement the static method createUI

package stackoverflow;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.RoundRectangle2D;
import javax.swing.AbstractButton;
import javax.swing.JComponent;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicButtonUI;

public class RoundedButtonUI extends BasicButtonUI {

    private static RoundedButtonUI shared;

    public static ComponentUI createUI(JComponent c) {
        if (shared != null) {
            return shared;
        }
        shared = new RoundedButtonUI();
        return shared;
    }

    @Override
    protected void installDefaults(AbstractButton b) {
        super.installDefaults(b);
        b.setOpaque(false);
        b.setBackground(BrandColors.SECOUNDARY);
        b.setForeground(BrandColors.TEXT_ON_SECOUNDARY);
    }

    @Override
    public void paint(Graphics g, JComponent c) {
        Graphics2D g2d = (Graphics2D) g.create();
        RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHints(hints);
        g2d.setColor(c.getBackground());
        g2d.fill(new RoundRectangle2D.Double(0, 0, c.getWidth() - 1, c.getHeight() - 1, 15, 15));
        g2d.setColor(c.getForeground());
        super.paint(g, c);
        g2d.dispose();
    }

}

Now, before I anything else, I need to install it, UIManager.getDefaults().put(new JButton().getUIClassID(), "stackoverflow.RoundedButtonUI"); . This should be done before you call any other UI related code (and after you've set the look and feel, if you're doing that)

And then I can just run the code as normal

在此处输入图像描述

import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                UIManager.getDefaults().put(new JButton().getUIClassID(), "stackoverflow.RoundedButtonUI");
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() {
            setBorder(new EmptyBorder(32, 32, 32, 32));
            setLayout(new GridBagLayout());

            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridwidth = GridBagConstraints.REMAINDER;
            gbc.insets = new Insets(4, 4, 4, 4);

            JButton button = new JButton("This is a normal button");
            add(button, gbc);
            JButton superButton = new JButton("This is a super button");
            add(superButton, gbc);
        }

    }

}

PLEASE NOTE

In order to install a new UI delegate this way, you MUST supply the fully qualified class name, that is, the full package path AND the class name.

In my examples above, I'm using stackoverflow as my package name (I'm lazy), so the installation looks like UIManager.getDefaults().put(new JButton().getUIClassID(), "stackoverflow.RoundedButtonUI");

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM