Java:更改UI字体(Nimbus)无效!

7

我正在参考这份Nimbus参考文档

我尝试将全局字体设置为稍大一些:

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

这个只对菜单起作用,而对于其他的元素(按钮、标签)没有效果。

我尝试使用以下代码来更改标签和按钮的字体:

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

但是字体保留不变。

对我而言,唯一有效的方法是派生一个字体:

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

但这并不是一个选项,因为必须手动为每个元素执行此操作。

请注意,为UIManager派生字体也不起作用

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

我在Linux和Windows下测试了所有内容:行为相同。

我真的无法理解一个API怎么会如此混乱。如果调用setFont(..)方法,那么我期望它会设置字体。如果这个方法在任何情况下都无法设置字体,那么应该将其弃用。

编辑:
这个问题不仅适用于Nimbus,还适用于默认LAF。


你尝试在更新UI默认值后调用“SwingUtilities.updateComponentTreeUI(frame);”了吗? - Rastislav Komara
那段代码甚至无法编译;没有一个字体构造函数接受浮点数作为第三个参数。 - Alan Moore
那段代码是示例代码,甚至不需要编译;) 在真正的代码中,我使用了静态变量,而在这个示例中我将它们删除后,我正在查看deriveFont(..)函数,该函数接受float类型参数。 - ivan_ivanovich_ivanoff
SwingUtilities.updateComponentTreeUI(frame) 也没有帮助 - ivan_ivanovich_ivanoff
1
实际上,Nimbus有"Label.font"属性,但当用户定义自定义值时,它不起作用。这可能是Nimbus/Synth的另一个bug。 我以前遇到过类似的问题,这就是为什么我选择Metal/Basic作为自定义LAF的基础。 - hfernandes
6个回答

7

这个代码可以在JDK6和JDK7上运行。复制+粘贴,玩得开心;)

注意:对于JDK6,请将
javax.swing.plaf.nimbus 改为
com.​sun.​java.​swing.​plaf.​nimbus

代码

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);
    }
   }

  });
 }

}

2
谢谢,我找到的所有提示中,这是唯一一个可以解决Nimbus错误的。 - Martin

6

nimbus默认值是懒加载的,因此在屏幕绘制之前设置'defaultFont'将字体添加到父默认值而不是nimbus默认值。

解决方法:强制nimbus初始化默认值并设置默认值:

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

注意:与上面建议的覆盖“getDefaults()”方法相比,此代码更为高效。

1
假设您已经设置了Nimbus LaF,以下是一行代码的答案(保留HTML标签): UIManager.getLookAndFeelDefaults().put("defaultFont", new Font(Font.SANS_SERIF, 0, 20)); 当然,在创建任何GUI组件之前,您需要调用此代码,也就是在设置Nimbus LaF后立即在主函数中调用。

1

有一件令我惊讶至今的事情是,LaF setters [setFont、setBackground等] 实际上并没有设置真正的属性。规范说明了 LaFs 可以忽略用户设置的字体、颜色等。这就是为什么 GTKLaF 完全失效了。它使用系统 gtk 主题设置,而不是程序员的设置。如果您计划以任何方式自定义外观,我建议永远不要使用 GTK 或 Nimbus LAF。

快速谷歌搜索可以找到this关于GTK的内容。

关于这些Nimbus问题的讨论可以在这里找到。


好的,设置这些属性对我来说确实有效!我引用我的问题:对我有效的唯一方法是派生字体:someButton.setFont(someButton.getFont().deriveFont(16f)); - ivan_ivanovich_ivanoff
但是执行 someComponent.setFont(..) 不是一个选项,因为这样的话,我必须为每个组件都这样做!这就像复制和粘贴代码一样。 - ivan_ivanovich_ivanoff

1

使用FontUIResource包装您的字体。我曾经遇到过与UIManager颜色相同的问题,而ColorUIResource解决了一切。在不深入JDK的情况下,我认为有些地方的组件期望(通过instanceof检查)UIResources(也许有人可以确认这一点)


嗯,这是值得一试的。如果你想改变颜色,记住它。 - basszero
使用UIManager.put("nimbusBase", new Color(someColor))可以完美地更改颜色。 - ivan_ivanovich_ivanoff

0

Java LAF API 有点笨拙,但查看源代码是获得答案的最佳途径。

请注意,MetalLookAndFeel 和 Nimbus 是不同的实现,每个实现的属性可能不相同。

以下示例使用 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);
      }
    });
  }
}

这个能够运作是因为在 Metal 上存在 "Label.font" 属性,并且正确地使用了该属性。

您可以通过以下方式进行检查:

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);
      }
    });
  }
}

注意:由于我在执行 UIManager.put("Label.font", UIManager.getFont("Label.font").deriveFont(16f)); 之前读取了该属性,因此必须存在 "Label.font" 这样的属性。 - ivan_ivanovich_ivanoff
实际上,你的例子是有效的!但只有在我不使用Nimbus UI而是使用跨平台LAF时才有效。 - ivan_ivanovich_ivanoff
那是一个Nimbus的bug。请查看http://www.jasperpotts.com/blog/2008/08/nimbus-uimanager-uidefaults/#comment-1351。 - hfernandes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接