简体   繁体   中英

How to set background of a JButton?

I am trying to set the color of a JButton with the properties in NetBeans with no success. When I select a color, only the border color change:

在此处输入图片说明

I have been reading about this and I think it is related to my Look and Feel. I created the project in Netbans as "Java Desktop Application", and I think I am using Nimbus:

public static void main(String args[]) {
    /* Set the Nimbus look and feel */
    //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
    /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
     * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
     */
    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 ex) {
        java.util.logging.Logger.getLogger(Entrenamiento.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (InstantiationException ex) {
        java.util.logging.Logger.getLogger(Entrenamiento.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (IllegalAccessException ex) {
        java.util.logging.Logger.getLogger(Entrenamiento.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (javax.swing.UnsupportedLookAndFeelException ex) {
        java.util.logging.Logger.getLogger(Entrenamiento.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    }
    //</editor-fold>

    /* Create and display the dialog */
    java.awt.EventQueue.invokeLater(new Runnable() {
        public void run() {
            Entrenamiento dialog = new Entrenamiento(new javax.swing.JFrame(), true);
            dialog.addWindowListener(new java.awt.event.WindowAdapter() {
                @Override
                public void windowClosing(java.awt.event.WindowEvent e) {
                    System.exit(0);
                }
            });
            dialog.setVisible(true);
        }
    });
}

I tried to set the background of the JButton in this way: UIManager.put("Button.background", Color.WHITE);

However it is not working. How can I achieve this? Thanks.

The customized Java L&F is a currently faced problem from a societies (like JFormDesigner) or Open Source programmers (like me).

The java l&f java customization is possible from three methods, like:

  • using UIManager
  • Using personal Component UI
  • using the manual configuration.

UIManager configuration

It is more complex and not simple because different l&f using different propriety inside the map. Also, you can commit errors inside the digit string, like "button.background" and not "Button.background".

Using custom component UI

You can use the personal component UI, like inside this answer but this has a sense with the custom change because if you change the l&f to runtime you lose this personal l&f.

ex: you want an specific button withoud mouse over effect, or without border

Create the manual configuration of the style.

You can use the manual configuration, like the answer , this method isn't good because you must organize the code with a methods or component like

public void configureStyle(String theme){
  //some code
}

Why the method to configure the style? because if you implement the possibility to change l&f inside your project without these methods or components you lose your personal setting.

ie: You must have an method for reset all personal proprieties.

Conclusion

I want to add a conclusion to my answer and it is: If you are developing a new project, I think you can use the new l&f to the base of your project and if you search it on Github you can find a lot of new l&f. But I want to talk in my answer one of my projects called MaterialTheming and implemented inside the Material-UI-Swing

MaterialTheming

MaterialTheming is a second layer for material-ui-swing l&f this consists to implement a component with all configuration of the l&f this component configurator is called theme .

So, at the moment MaterialTheming is in the alpha-version and supports only procedural setting. Also, with MaterialTheming you can develop your personal theme and you have the complete control of the style of L&f.

I have developer this for necessary to implement the lite and dark theme with the same interface and with the methods inside java I wrote a lot of code inside my client application (this is a bad choise because the GUI of application).

An fist (partial example) of personal theme is this called MaterialDarkPinkTheme

So, to implement an theme for your application to change the default color button, the code is like this

import mdlaf.themes.MaterialLiteTheme;
import mdlaf.utils.MaterialColors;

/**
 * @author https://github.com/vincenzopalazzo
 */
public class PersonalMaterialTheme extends MaterialLiteTheme {

    @Override
    protected void installColor() {
        super.installColor();
        super.buttonBackgroundColor = MaterialColors.AMBER_800;
        super.buttonBackgroundColorMouseHover = MaterialColors.AMBER_500;
        super.buttonDefaultBackgroundColor = MaterialColors.COSMO_ORANGE;
    }
}

The effect is this

在此处输入图片说明

Maybe this is a dumb question, but have you tried just setting the background color via code? Like this:

yourButton.setOpaque(true);
yourButton.setBackground(Color.WHATEVER);

Ok, I finally got it, this Windows UI is really hard to alter.
So if you want to use the Nimbus Look and Feel you should obviously find out why it is replaced by the Windows Look and Feel.

But if you want to stick with the Windows L&F you can define a new UI for the Button class, that is like the WindowsButtonUI , but takes the background color into account.
For that I created a UI class that calls the Windows L&F if available. This was especially hard because the Windows L&F and all its classes are not in the standard distribution and therefore cannot be used in source code, so no exteds with overrides, just delegating with reflection.
I expanded it by letting the WindowsButtonUI paint onto a buffer, then replacing the windows button color with the set background color, but keeping all effects painted on top of the background (those blue effects when you select, click etc.). Also still paints the slightly darker border, but now with a slightly darker version of your custom color.
While the code is the opposite of what you should do (with a lot of reflection calls) and the Color replace seems to not do exactly what I expected (fixed the issue) , I do think the result looks pretty good.

Note: This may look really wierd on any operating system apart from Windows.

To set the new UI and Color, do:

UIManager.put("Button.background", Color.WHITE);
UIManager.put("ButtonUI", CustomWindowsButtonUI.class.getCanonicalName());

And the sourcecode of CustomWindowsButtonUI is:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.lang.reflect.Method;

import javax.swing.AbstractButton;
import javax.swing.JComponent;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicButtonUI;
import javax.swing.plaf.metal.MetalButtonUI;

import com.sun.java.swing.plaf.windows.WindowsGraphicsUtils;

import sun.awt.AppContext;

@SuppressWarnings("restriction")
public class CustomWindowsButtonUI extends BasicButtonUI {

    private final static Color WINDOWS_BUTTON_COLOR = new Color(225, 225, 225);

    private BasicButtonUI delegateUI;
    private Class<?> windowsUIClass = null;
    private boolean isWindows = false;

    private static final Object CUSTOM_WINDOWS_BUTTON_UI_KEY = "CUSTOM_WINDOWS_BUTTON_UI_KEY";

    public CustomWindowsButtonUI() {
        try {
            windowsUIClass = Class.forName("com.sun.java.swing.plaf.windows.WindowsButtonUI");
            delegateUI = (BasicButtonUI) windowsUIClass.getConstructor().newInstance();
            isWindows = true;
        } catch (Throwable e) {
            System.err.println("Failed to locate windows button ui, defaulting to MetalUI (Swing default).");
            delegateUI = new MetalButtonUI();
        }
    }

    public static ComponentUI createUI(JComponent c) {
        AppContext appContext = AppContext.getAppContext();
        CustomWindowsButtonUI customWindowsButtonUI = (CustomWindowsButtonUI) appContext.get(CUSTOM_WINDOWS_BUTTON_UI_KEY);
        if (customWindowsButtonUI == null) {
            customWindowsButtonUI = new CustomWindowsButtonUI();
            appContext.put(CUSTOM_WINDOWS_BUTTON_UI_KEY, customWindowsButtonUI);
        }
        return customWindowsButtonUI;
    }

    @Override
    public void update(Graphics g, JComponent c) {
        paint(g, c);
    }

    @Override
    public void paint(Graphics g, JComponent c) {
        if (isWindows) {
            int width = c.getWidth();
            int height = c.getHeight();
            BufferedImage paintBufferImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
            delegateUI.paint(paintBufferImage.createGraphics(), c);
            Color backgroundColor = c.getBackground();
            for (int x = 0; x < width; x++) {
                for (int y = 0; y < height; y++) {
                    Color pixelColor = new Color(paintBufferImage.getRGB(x, y), true);
                    Color targetColor = new Color(getTargetColorValue(pixelColor.getRed(), WINDOWS_BUTTON_COLOR.getRed(), backgroundColor.getRed()),
                            getTargetColorValue(pixelColor.getGreen(), WINDOWS_BUTTON_COLOR.getGreen(), backgroundColor.getGreen()),
                            getTargetColorValue(pixelColor.getBlue(), WINDOWS_BUTTON_COLOR.getBlue(), backgroundColor.getBlue()), pixelColor.getAlpha());
                    paintBufferImage.setRGB(x, y, targetColor.getRGB());
                }
            }
            g.drawImage(paintBufferImage, 0, 0, null);
        } else {
            delegateUI.paint(g, c);
        }
    }

    private static int getTargetColorValue(int value, int subtract, int add){
        return Math.min(255, Math.max(0, value - subtract + add));
    }

    protected void installDefaults(AbstractButton b) {
        try {
            Method method = windowsUIClass.getDeclaredMethod("installDefaults", AbstractButton.class);
            method.setAccessible(true);
            method.invoke(delegateUI, b);
        } catch (Throwable e) {
            System.err.println("Failed to call delegate installDefaults: " + e.getClass().getName() + " - " + e.getMessage());
            super.installDefaults(b);
        }
    }

    protected void uninstallDefaults(AbstractButton b) {
        try {
            Method method = windowsUIClass.getDeclaredMethod("uninstallDefaults", AbstractButton.class);
            method.setAccessible(true);
            method.invoke(delegateUI, b);
        } catch (Throwable e) {
            System.err.println("Failed to call delegate uninstallDefaults: " + e.getClass().getName() + " - " + e.getMessage());
            super.uninstallDefaults(b);
        }
    }

    // copied from WindowsButtonUI
    protected void paintText(Graphics g, AbstractButton b, Rectangle textRect, String text) {
        WindowsGraphicsUtils.paintText(g, b, textRect, text, getTextShiftOffset());
    }

    protected void paintFocus(Graphics g, AbstractButton b, Rectangle viewRect, Rectangle textRect, Rectangle iconRect) {
        try {
            Method method = windowsUIClass.getDeclaredMethod("paintFocus", Graphics.class, AbstractButton.class, Rectangle.class, Rectangle.class, Rectangle.class);
            method.setAccessible(true);
            method.invoke(delegateUI, g, b, viewRect, textRect, iconRect);
        } catch (Throwable e) {
            System.err.println("Failed to call delegate paintFocus: " + e.getClass().getName() + " - " + e.getMessage());
            super.paintFocus(g, b, viewRect, textRect, iconRect);
        }
    }

    // copied from WindowsButtonUI
    protected void paintButtonPressed(Graphics g, AbstractButton b) {
        setTextShiftOffset();
    }

    // copied from WindowsButtonUI
    public Dimension getPreferredSize(JComponent c) {
        Dimension d = super.getPreferredSize(c);
        AbstractButton b = (AbstractButton) c;
        if (d != null && b.isFocusPainted()) {
            if (d.width % 2 == 0) {
                d.width += 1;
            }
            if (d.height % 2 == 0) {
                d.height += 1;
            }
        }
        return d;
    }

}

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