简体   繁体   中英

Java: Altering UI fonts (Nimbus) doesn't work!

I'm referring to this Nimbus reference .

I tried to set global Font to be slightly larger:

UIManager.put("defaultFont", new Font(Font.SANS_SERIF, 0, 16));

...works only for the menu but nothing else (buttons, labels).

I tried to change labels and buttons fonts with

UIManager.put("Button.font", new Font(Font.SANS_SERIF, 0, 16));
UIManager.put("Label.font", new Font(Font.SANS_SERIF, 0, 16));

but the font remains .

The only thing that worked for me was deriving a font:

someButton.setFont(someButton.getFont().deriveFont(16f));

But this is not an option, since this must be done for each element manually.

Note, that deriving a font for UIManager doesn't work either:

UIManager.put("Label.font",
    UIManager.getFont("Label.font").deriveFont(16f));

I tested everything under Linux and Windows: same behavior.

I just can't understand how an API can be so messy. If a method is called setFont(..) then I expect it to set the font. If this method fails to set the font in any thinkable circumstances, then it should be deprecated.

EDIT:
The problem not only applies to Nimbus, but also to the default LAF.

This works with JDK6 and JDK7. Copy+paste and have fun ;)

Note: for JDK6, change
javax.swing.plaf.nimbus to
com.​sun.​java.​swing.​plaf.​nimbus .

Code

import java.awt.*;
import java.lang.reflect.*;
import javax.swing.*;
import javax.swing.plaf.nimbus.*;

public class Main {

 public static void main(String[] args)
   throws InterruptedException, InvocationTargetException {

  SwingUtilities.invokeAndWait(new Runnable() {

   @Override
   public void run() {
    try {
     UIManager.setLookAndFeel(new NimbusLookAndFeel() {

      @Override
      public UIDefaults getDefaults() {
       UIDefaults ret = super.getDefaults();
       ret.put("defaultFont",
         new Font(Font.MONOSPACED, Font.BOLD, 16)); // supersize me
       return ret;
      }

     });

     new JFrame("Hello") {

      {
       setDefaultCloseOperation(EXIT_ON_CLOSE);
       setLayout(new FlowLayout(FlowLayout.LEFT));

       setSize(500, 500);
       setLocationRelativeTo(null);

       add(new JLabel("someLabel 1"));
       add(new JButton("someButton 1"));
       add(new JLabel("someLabel 2"));
       add(new JButton("someButton 2"));

       setVisible(true);
      }

     };     
    } catch (Exception ex) {
     throw new Error(ex);
    }
   }

  });
 }

}

The nimbus defaults are lazy created so setting 'defaultFont' before the screen is painted, will add the font to the parent defaults, and not to the nimbus defaults.

Workaround: force nimbus to initialize the defaults and set then the defaults:

NimbusLookAndFeel laf = new NimbusLookAndFeel();
UIManager.setLookAndFeel(laf);
laf.getDefaults().put("defaultFont", new Font("Monospaced", Font.BOLD, 12));

Note: this code is more efficient then overriding getDefaults(), as suggested above.

The answer in a single line of code (assuming you have already set Nimbus LaF):

UIManager.getLookAndFeelDefaults().put("defaultFont", new Font(Font.SANS_SERIF, 0, 20));

Of course, you need to call this before you create any GUI components, ie right in your main just after setting Nimbus LaF.

One thing that amazes me to this day is that the LaF setters [setFont, setBackground, etc] do not actually set real properties. The spec says that LaFs are allowed to ignore user set fonts, colors, etc. This is why GTKLaF is completely broken. It uses the system gtk theme settings, not the programmer's settings. IIRC Nimbus has a separate, package private class that contains defaults (NimbusDefaults?) and can't be easily accessed.

I suggest never using GTK or Nimbus LAF if you plan on customizing the look in any way.

A quick google search turns up this for GTK

A discussion about these problems in nimbus can be found here .

Wrap your Font with FontUIResource . I had the exact same problem with UIManager colors and ColorUIResource fixed everything. Without digging through the JDK, I think there are some places where components expect (read: check via instanceof) for UIResources (maybe someone can confirm this)

Java LAF API is a little bit clumsy, but there is nothing better than check the source code to get your answers.

Note that MetalLookAndFeel and Nimbus are different implementations and the properties for each one doesn't have to be the same.

The following examples use the MetalLookAndFeel.

package com.stackoverflow.laf.font;

import java.awt.Font;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

public class SetFontExample {

  public static void main(String[] args) {

    SwingUtilities.invokeLater(new Runnable() {
      @Override public void run() {
        UIManager.put("Label.font", new Font(Font.SANS_SERIF, 0, 20));
        try {
          UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
        } catch (Exception e) {
          e.printStackTrace();
        }

        JFrame frame = new JFrame("Set font example");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new JLabel("Font test"));
        frame.pack();
        frame.setVisible(true);
      }
    });
  }
}

This works because the property "Label.font" exists on Metal and it uses that property correctly.

You you can check it this way:

package com.stackoverflow.laf;

import javax.swing.SwingUtilities;
import javax.swing.UIDefaults;
import javax.swing.UIManager;

public class ListLAFUIDefaults {

  public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      @Override public void run() {
        try {
          // Choose LAF
          UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
        } catch (Exception e) {
          e.printStackTrace();
        }
        UIDefaults defaults = UIManager.getLookAndFeel().getDefaults();
        System.out.println(defaults);

        // Check a property
        String propertyKey = "defaultFont";
        System.out.println(UIManager.getLookAndFeel().getName() +
            (defaults.containsKey(propertyKey) ? " contains " : " doesn't contain ") +
            "property " + propertyKey);
      }
    });
  }
}

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