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 :
-
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 callsuper.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
methodcreateUI
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 likeUIManager.getDefaults().put(new JButton().getUIClassID(), "stackoverflow.RoundedButtonUI");