I'd like to design a Swing button that would look like an UWP one – like so [Windows Settings App]:

This is what I have so far:
using the following code:
Font f = new Font("Segoe UI", Font.PLAIN, 20);
Color gray = new Color(204, 204, 204);
button.setFont(f);
button.setBackground(gray);
button.setContentAreaFilled(false);
button.setFocusPainted(false);
button.setFocusable(false);
button.setForeground(Color.BLACK);
button.setBorder(BorderFactory.createEmptyBorder(10, 14, 10, 14));
The background color cannot be changed at all, regardless whether button.setContentAreaFilled(Boolean b); property is set to false or true, since the EmptyBorder inherits the default color for Windows Swing buttons.
Hovering (color change on hover) also stops to function with the property set to false.
Setting button.setContentAreaFilled(true); gives the following result which is also not ideal, since the button's background still isn't changed from the default color (+it has an outline):
My question is, basically: how to modify my code in order to get the following UWP–like design?
default:
hover:
There are a number of ways you "might" do this, you could
- Create a factory method to apply the properties and listeners you need to mimic the functionality
- Create a new class which extends from
JButtonorAbstractButtonand provide core functionality/properties you need in a self contained package - You could provide you own UI delegate, and customise the look and feel of the button at the core
Each method has it's pros and cons and you need to decide which better meets you overall needs.
For example, it's much easier to supply a customised look and feel delegate into an existing code base, as you don't need to change the code, other then to install the look and feel when you want to use it
The following is an example of using a look and feel delegate, this is just a proof of concept and there are probably a lot of additional functionality/work that needs to be done - for example, I'd like to supply more hints about the colors to use and the thickness of the roll over border, for example
import java.awt.Color;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javaapplication24.Test.MetroLookAndFeel;
import javax.swing.AbstractButton;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.plaf.basic.BasicButtonUI;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setBorder(new EmptyBorder(10, 10, 10, 10));
JButton fancyPB = new JButton("Restart Now");
fancyPB.setUI(new MetroLookAndFeel());
JButton normalPB = new JButton("Restart Now");
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(4, 4, 4, 4);
add(fancyPB, gbc);
add(normalPB, gbc);
}
}
public class MetroLookAndFeel extends BasicButtonUI {
// This could be computed properties, where the border color
// is determined based on other properties
private Border focusBorder = new CompoundBorder(new LineBorder(Color.DARK_GRAY, 3), new EmptyBorder(7, 13, 7, 14));
private Border unfocusedBorder = new EmptyBorder(10, 14, 10, 14);
@Override
protected void installDefaults(AbstractButton b) {
super.installDefaults(b);
Font f = new Font("Segoe UI", Font.PLAIN, 20);
Color gray = new Color(204, 204, 204);
b.setFont(f);
b.setBackground(gray);
b.setContentAreaFilled(false);
b.setFocusPainted(false);
// This seems like an oddity...
b.setFocusable(false);
b.setForeground(Color.BLACK);
// b.setBorder(BorderFactory.createEmptyBorder(10, 14, 10, 14));
b.setBorder(unfocusedBorder);
}
@Override
protected void installListeners(AbstractButton b) {
super.installListeners(b);
b.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
((JButton)e.getSource()).setBorder(focusBorder);
}
@Override
public void mouseExited(MouseEvent e) {
((JButton)e.getSource()).setBorder(unfocusedBorder);
}
});
}
}
}
Here is an example
import java.awt.Color;
import java.awt.Font;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
import javax.swing.border.CompoundBorder;
import javax.swing.border.LineBorder;
public class WinButton {
public static void main(String[] args) {
final JButton button = createWinButton("Restart now");
JFrame frm = new JFrame("Test");
JPanel layoutPanel = new JPanel();
layoutPanel.add(button);
frm.add(layoutPanel);
frm.setSize(200, 200);
frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frm.setLocationRelativeTo(null);
frm.setVisible(true);
}
private static JButton createWinButton(String text) {
final JButton button = new JButton(text);
Font f = new Font("Segoe UI", Font.PLAIN, 20);
Color gray = new Color(204, 204, 204);
button.setFont(f);
button.setBackground(gray);
button.setContentAreaFilled(false);
button.setFocusPainted(false);
button.setFocusable(false);
button.setForeground(Color.BLACK);
button.setOpaque(true);
button.setBorder(BorderFactory.createEmptyBorder(10, 14, 10, 14));
button.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
Color borderColor = new Color(100, 100, 100);
button.setBorder(new CompoundBorder(new LineBorder(borderColor, 3), BorderFactory.createEmptyBorder(7, 11, 7, 11)));
}
@Override
public void mouseExited(MouseEvent e) {
button.setBorder(BorderFactory.createEmptyBorder(10, 14, 10, 14));
}
});
return button;
}
}
In order to manipulate mouse events, such as hovering, you'll have to treat these events yourself, one of the ways to do is to create your own button.
Edit
Adding a mouse listener to your button, as answered by Sergiy Medvynskyy, would be a better practice since there is no need to tinker with the button class itself.
You may also have to play with the LaF (UIManager Look and Feel) since it can add undesired effects to UI components.
note that this can affect all of the interface components.
Following is a basic example.
Button:
import java.awt.Color;
import java.awt.Font;
import java.awt.event.MouseEvent;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.border.Border;
public class UWPButton extends JButton{
private final Border regularBorder;
private final Border hoverBorder;
private final Color bgColor = new Color(204, 204, 204);
private final Color transparent;
private final Font metroFont = new Font("Segoe UI", Font.PLAIN, 20);
public UWPButton() {
super();
// thickness of the button's borders
int thickness = 4;
// Create a transparent color, to use for the regular border
// could also be the bgColor (204 204 204)
Color c = new Color(1f, 0f, 0f, 0f);
transparent = c;
// Creates a compound border to be present on the button majority of the time.
// uses an invisible line border on the outside in order to change border smoothly
// the inside border is just an empty border for insets.
regularBorder = BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(transparent, thickness), //outside
BorderFactory.createEmptyBorder(10, 14, 10, 14)); //inside
// Creates a compound border which will be used when the mouse hovers the button.
// the outside border has a darker colour than the background
// the inside border is just an empty border for insets.
hoverBorder = BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(bgColor.darker(), thickness), //outside
BorderFactory.createEmptyBorder(10, 14, 10, 14)); //inside
// configures the button
initButton();
}
// Here is where the mouse events are treated.
@Override
protected void processMouseEvent(MouseEvent e) {
// Gets the ID of the event,
// if it is a mouse entering the button, sets the new border
// if it is the mouse exiting the button, resets the border to the default.
switch(e.getID()){
case MouseEvent.MOUSE_ENTERED:
this.setBorder(hoverBorder);
break;
case MouseEvent.MOUSE_EXITED:
this.setBorder(regularBorder);
break;
}
// the parent then does all the other handling.
super.processMouseEvent(e);
}
// Configures the button.
private void initButton(){
setFont(metroFont);
setBorder(regularBorder);
setBackground(bgColor);
setFocusPainted(false);
}
}
Changing the LaF: If you created your frame through an IDE drag and drop, you should have something like this inside your main (this is from netbeans):
CHANGE "Nimbus" to "Metro"(or remove the LaF setting) so the interface manager won't render all the Nimbus stuff. Note: there is no "Metro" LaF, this is just to disable the applied LaF.
// other stuff above
//
// change "Nimbus" for something else or just remove this try-catch block
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
// other stuff bellow
More on the LaF (Look and Feel): https://docs.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
来源:https://stackoverflow.com/questions/46448044/swing-create-a-uwp-metro-like-button





