如何在Swing GTK外观中更改默认字体大小?

5
有没有办法在Swing GTK LaF中更改默认字体大小?
GTK LaF似乎假定72dpi,因此在使用96dpi屏幕时,所有字体的大小都只有应有大小的3/4。有关详细信息,请参见Fedora bug。在等待修复期间,我想找到一种解决方法。
例如,我已经尝试通过UIDefaults重置字体大小,如here所建议的那样,但是(正如也在那里指出的那样),GTK LaF似乎忽略了这一点。
我“可以”构建一个小部件工厂,该工厂还将设置所需的字体大小以创建所有我的Swing小部件,但这将是极其具有侵入性的,因此如果有其他方法,我想避免这条路。 编辑:以下内容无效:
public class GTKLaF extends com.sun.java.swing.plaf.gtk.GTKLookAndFeel {
  @Override
  public UIDefaults getDefaults() {
    final float scale = 3f;

    final UIDefaults defaults = super.getDefaults(); 

    final Map<Object,Object> changes = new HashMap<Object,Object>();

    for (Map.Entry<Object,Object> e : defaults.entrySet()) {
      final Object key = e.getKey();
      final Object val = e.getValue();

      if (val instanceof FontUIResource) {
        final FontUIResource ores = (FontUIResource) val;
        final FontUIResource nres =
          new FontUIResource(ores.deriveFont(ores.getSize2D()*scale));
        changes.put(key, nres);
        System.out.println(key + " = " + nres);
      } 
      else if (val instanceof Font) {
        final Font ofont = (Font) val;
        final Font nfont = ofont.deriveFont(ofont.getSize2D()*scale);
        changes.put(key, nfont);
        System.out.println(key + " = " + nfont);
      }
    }

    defaults.putAll(changes);

    return defaults;
  }
}

你可能认为这会打印至少十二个键值对,但实际上只打印一个:TitledBorder.font。显然,GTLLookAndFeel没有提供其他字体属性,而是来自其他地方!
4个回答

1
我的建议是创建自己的GTKLookAndFeel子类并使用它。
在您的子类中,我可能会覆盖getDefaults()。在您的getDefaults()中,如果需要,可以获取GTKLookAndFeel将返回的UIDefaults并进行调整(通过包装或子类化它们并覆盖UIDefaults.getFont方法)。
使用

获取当前DPI


int dpi = Toolkit.getDefaultToolkit().getScreenResolution();

如果不是72,您可以相应地更改字体大小。

编辑:您发布的链接无效,我认为这是因为GTKLookAndFeel覆盖了涉及的方法,并且不允许其祖先的任何默认值通过。反过来覆盖它应该允许您规避该问题。


请查看我对原问题的编辑。覆盖getDefaults()方法不起作用,因为键不存在。 - uckelman
这一定是一个设计非常糟糕的外观。虽然我没有查看源代码,但我开始感觉他们只是硬编码了值。作为最后的手段,你可以将GTKLookAndFeel代码复制到自己的类中并进行修改。 - Brad Mace
是的,我认为这是一个相当糟糕的问题。我给你悬赏是因为(1)时间快到了,(2)你离答案最近。(我的怀疑是,要想正确解决这个问题,可能需要覆盖更多的方法,可能还涉及到com.sun.java.swing.plaf.gtk中的其他类。) - uckelman

1

似乎没有办法设置默认字体大小。然而,问题中提到的错误已经被修复,因此现在不需要这样做了。


0

我浪费了很多时间来弄清楚如何使我们的应用程序在Ubuntu上表现良好,最终找到了这个方法。

有人建议我尝试扩展GTKLookAndFeel,但由于它使用许多本地调用到GTKEngine,我意识到这是不可行的。

希望这可以帮到你。

在设置外观后,立即调用我的hack checkGTKLookAndFeel(),它会将字体标准降低两个点:

UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
try {
    InvoicexUtil.checkGTKLookAndFeel();
} catch (Exception e) {
    e.printStackTrace();
}

public static void checkGTKLookAndFeel() throws Exception {
    LookAndFeel look = UIManager.getLookAndFeel();
    if (!look.getID().equals("GTK")) return;

    new JFrame();
    new JButton();
    new JComboBox();
    new JRadioButton();
    new JCheckBox();
    new JTextArea();
    new JTextField();
    new JTable();
    new JToggleButton();
    new JSpinner();
    new JSlider();
    new JTabbedPane();
    new JMenu();
    new JMenuBar();
    new JMenuItem();

    Object styleFactory;
    Field styleFactoryField = look.getClass().getDeclaredField("styleFactory");
    styleFactoryField.setAccessible(true);
    styleFactory = styleFactoryField.get(look);

    Field defaultFontField = styleFactory.getClass().getDeclaredField("defaultFont");
    defaultFontField.setAccessible(true);
    Font defaultFont = (Font) defaultFontField.get(styleFactory);
    FontUIResource newFontUI;
    newFontUI = new FontUIResource(defaultFont.deriveFont((float)(defaultFont.getSize() - 2f)));
    defaultFontField.set(styleFactory, newFontUI);

    Field stylesCacheField = styleFactory.getClass().getDeclaredField("stylesCache");
    stylesCacheField.setAccessible(true);
    Object stylesCache = stylesCacheField.get(styleFactory);
    Map stylesMap = (Map) stylesCache;
    for (Object mo : stylesMap.values()) {
        Field f = mo.getClass().getDeclaredField("font");
        f.setAccessible(true);
        Font fo = (Font)f.get(mo);
        f.set(mo, fo.deriveFont((float)(fo.getSize() - 2f)));
    }
}

调用各种Swing组件的构造函数来初始化GTK样式。 不是很优雅,但它可以工作


0

您提供的代码存在问题。UIDefaults.get(key)函数不一定会返回Font实例,它可能是一个javax.swing.plaf.FontUIResource实例。 以下代码片段查找所有已安装的外观,并以scale比例更改所有字体大小。

float scale=1.1f;
UIManager.LookAndFeelInfo looks[] = UIManager.getInstalledLookAndFeels();

for (UIManager.LookAndFeelInfo info : looks) {

    //if you want to change LaF to a spesific LaF,such as "GTK"
    //put here a if statement like:
    //if(info.getClassName().contains("GTK"))
    UIManager.setLookAndFeel(info.getClassName());

    UIDefaults defaults = UIManager.getDefaults();
    Enumeration newKeys = defaults.keys();

    while (newKeys.hasMoreElements()) {
        Object obj = newKeys.nextElement();
        Object current = UIManager.get(obj);
        if (current instanceof FontUIResource) {
            FontUIResource resource = (FontUIResource) current;
            defaults.put(obj, new FontUIResource(resource.deriveFont(resource.getSize2D()*scale)));
            // System.out.printf("%50s : %s\n", obj,  UIManager.get(obj));
        } else if (current instanceof Font) {
            Font resource = (Font) current;
            defaults.put(obj, resource.deriveFont(resource.getSize2D()*scale));
            // System.out.printf("%50s : %s\n", obj,  UIManager.get(obj));
        }
    }
}

我在一本书中找到了关于GTK LaF问题的内容,该书名为《Java Examples in a Nutshell, Third Edition》。你可以在这里看一下。


这个不起作用。对于GTK LaF,无论我将比例因子设置为什么,都没有任何影响-这段代码没有明显的效果。 - uckelman
调用UIManager.getInstalledLookAndFeels()似乎会导致X的灾难性崩溃,不知何故——机器完全死机。如果我删除它和外部循环,您的代码确实可以在Metal LaF上工作。 - uckelman
回答已更新,答案底部有一个链接解释GTK问题。请尝试并告诉我是否有效。 - Ferid Gürbüz
那个链接是关于确保系统知道GTL LaF的情况。这不是我面临的问题。当我运行时设置了-Dswing.defaultlaf=com.sun.java.swing.plaf.gtk.GTKLookAndFeel,我确实得到了安装的GTK LaF。在这种情况下,UIManager.getSystemLookAndFeelClassName()返回com.sun.java.swing.plaf.gtk.GTKLookAndFeel。问题是GTK LaF只是忽略我放入UIManager的键。 - uckelman

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