Can anybody tell me, why in the following code setting a button's background fails when using a color retrieved from UIManager?
[This question is obviously too short to be accepted by Stackoverflow, but I have no more details to present.]
import java.awt.*;
import javax.swing.*;
import javax.swing.plaf.*;
public class BackgroundColor extends JFrame {
public BackgroundColor() {
setSize(400,300);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setLayout(new GridLayout(0, 1, 0, 5));
setLocationRelativeTo(null);
JButton b= new JButton("Default");
add(b);
b= new JButton("" Color.RED);
b.setBackground(Color.RED);
add(b);
b= new JButton("Failed: " new ColorUIResource(255, 0, 0)); // RED
b.setBackground(new ColorUIResource(255, 0, 0));
add(b);
b= new JButton("Failed: " UIManager.getColor("MenuItem.background"));
b.setBackground(UIManager.getColor("MenuItem.background"));
add(b);
b= new JButton("Failed: " UIManager.getColor("menu"));
b.setBackground(UIManager.getColor("menu"));
add(b);
b= new JButton("" new Color(238, 238, 238));
b.setBackground(new Color(238, 238, 238));
add(b);
setVisible(true);
}
public static void main(String arg[]) {
EventQueue.invokeLater(BackgroundColor::new);
}
}
CodePudding user response:
Colors of type ColorUIResource
have a special meaning: they are default colors, installed by LookAndFeel. And they may be interpret by a non-standard way.
Painting of button performes the current LookAndFeel
. In your case: MetalLookAndFeel
, or, more precise MetalButtonUI
. Painting of button is defined in method update
as following:
public void update(Graphics g, JComponent c) {
AbstractButton button = (AbstractButton)c;
if ((c.getBackground() instanceof UIResource) &&
button.isContentAreaFilled() && c.isEnabled()) {
ButtonModel model = button.getModel();
if (!MetalUtils.isToolBarButton(c)) {
if (!model.isArmed() && !model.isPressed() &&
MetalUtils.drawGradient(
c, g, "Button.gradient", 0, 0, c.getWidth(),
c.getHeight(), true)) {
paint(g, c);
return;
}
}
else if (model.isRollover() && MetalUtils.drawGradient(
c, g, "Button.gradient", 0, 0, c.getWidth(),
c.getHeight(), true)) {
paint(g, c);
return;
}
}
super.update(g, c);
}
As you can see, if button has background of class ColorUIResource, this method provides painting of foreground only (probably due to performance reasons) by calling of method paint
. And if you set a normal color as background, you avoid this behavior and call the line super.update(g, c);
And here is the implementation of this method (defined in class ComponentUI
):
public void update(Graphics g, JComponent c) {
if (c.isOpaque()) {
g.setColor(c.getBackground());
g.fillRect(0, 0, c.getWidth(),c.getHeight());
}
paint(g, c);
}
Conclusion: do not use the class ColorUIResource
, except you write your own LookAndFeel