我希望为某些Swing应用程序添加Hi-DPI支持,但我找不到足以满足我需求的解决方案.我需要支持多种外观和感觉,所以情况看起来比我发现的其他帖子复杂得多(这往往暗示“调整你的UI大小以匹配你的字体大小”).
一些实验发现,UIManager包含许多可以调整的指标,以帮助您在使应用程序Hi-DPI友好的良好开端. (UIManager-Defaults实用程序对于探索这些实用程序非常宝贵!)我发现,L& Fs似乎完全不同于彼此:
> Windows L& F为您提供了一个很好的(不完美的)默认字体大小,并且内置图标(如复选框和窗口图标)的大小适当 – 但许多其他指标仍然没有出现问题.
>在Metal中,您可以单独更新UIManager中的字体.通过一些工作,您可以扩展内置的IconUIResources以匹配.
>在Nimbus中你可以只更新一个默认字体,其他字体就位了……但是我不知道如何缩放内置图标并成功渲染组合框,单选按钮(等)!
我从玩耍中获得的感觉是,应该可以独立地为每个L& F特定的特定调整创建列表.这可能包括调整Font,Icon,Integer和Dimensions的默认值.
有没有人想出一个很好的解决方案呢?
任何人都可以分享一个明确的清单,其中UIDefaults需要调整标准L& Fs吗?
我会很高兴能够始终如一地支持Metal和Windows的解决方案.
我想这样的解决方案应该是可以重复使用的.可以解决一系列Swing应用程序的相同问题.我很惊讶没有这样的效用似乎存在. (如果不是,请赐教!)这种方法当然不会解决所有问题(例如,你仍然需要手动扩展任何对setPreferredSize等的调用.然后,再次支持多种L& Fs的应用程序应该避免调用无论如何.)不过,我想,它可以让很多应用程序开始.
我知道JDK-9承诺完整Hi-DPI support,但我不能等待那么久 – 即使在2017年发布之后,我也可能无法切换一段时间.
最佳答案 这是基于我最初的原型设计的解决方案.
我对某些部分不满意,例如“整数”和“字体”修改的规则非常“神奇”.这是我希望从其他人那里获得更多Swing / L& F体验的地方.
我已经把它变成了一个社区wiki,所以如果人们更喜欢迭代这个并添加他们的知识,而不是发布他们自己的解决方案,那么请随意.
我开始使用可以修改某些ui-defaults的界面:
public interface Tweaker {
void initialTweaks();
Font modifyFont(Object key, Font original);
Icon modifyIcon(Object key, Icon original);
Integer modifyInteger(Object key, Integer original);
}
可以像这样调用:
public void scaleUiDefaults() {
float dpiScale = Toolkit.getDefaultToolkit().getScreenResolution() / 96f;
Tweaker delegate = createTweakerForCurrentLook(dpiScale);
tweakUiDefaults(delegate, dpiScale);
}
private Tweaker createTweakerForCurrentLook(float dpiScaling) {
String testString = UIManager.getLookAndFeel().getName().toLowerCase();
if (testString.contains("windows")) return new WindowsTweaker(dpiScaling);
if (testString.contains("nimbus")) return new NimbusTweaker(dpiScaling);
return new BasicTweaker(dpiScaling);
}
private void tweakUiDefaults(Tweaker delegate, float multiplier) {
UIDefaults defaults = UIManager.getLookAndFeelDefaults();
delegate.initialTweaks();
for (Object key: Collections.list(defaults.keys())) {
Object original = defaults.get(key);
Object newValue = getUpdatedValue(delegate, key, original);
if (newValue != null && newValue != original) {
defaults.put(key, newValue);
}
}
}
private Object getUpdatedValue(Tweaker delegate, Object key, Object original) {
if (original instanceof Font) return delegate.modifyFont(key, (Font) original);
if (original instanceof Icon) return delegate.modifyIcon(key, (Icon) original);
if (original instanceof Integer) return delegate.modifyInteger(key, (Integer) original);
return null;
}
我把大多数L& Fs看起来使用的功能放在基类中.这似乎处理金属没有进一步细化:
public class BasicTweaker {
protected final float scaleFactor;
protected final UIDefaults uiDefaults = UIManager.getLookAndFeelDefaults();
public BasicTweaker(float scaleFactor) {
this.scaleFactor = scaleFactor;
}
public void initialTweaks() {}
public Font modifyFont(Object key, Font original) {
// Ignores title & accelerator fonts (for example)
if (original instanceof FontUIResource && key.toString().endsWith(".font")) {
return newScaledFontUIResource(original, scaleFactor);
}
return original;
}
protected static FontUIResource newScaledFontUIResource(Font original, float scale) {
int newSize = Math.round(original.getSize() * scale);
return new FontUIResource(original.getName(), original.getStyle(), newSize);
}
public Icon modifyIcon(Object key, Icon original) {
return new IconUIResource(new ScaledIcon(original, scaleFactor));
}
public Integer modifyInteger(Object key, Integer original) {
if (!endsWithOneOf(lower(key), LOWER_SUFFIXES_FOR_SCALED_INTEGERS)) {
return original;
}
return (int) (original * scaleFactor);
}
private boolean endsWithOneOf(String text, String[] suffixes) {
return Arrays.stream(suffixes).anyMatch(suffix -> text.endsWith(suffix));
}
private String lower(Object key) {
return (key instanceof String) ? ((String) key).toLowerCase() : "";
}
private static final String[] LOWER_SUFFIXES_FOR_SCALED_INTEGERS =
new String[] { "width", "height", "indent", "size", "gap" };
}
上面使用的类ScaledIcon可能超出范围 – 但它实际上只是调用ImageIcon(图像).paintIcon,其宽度和宽度都是修改过的.高度.
然后覆盖其他L& Fs的BasicTweaker ……
视窗:
public class WindowsTweaker extends BasicTweaker {
public WindowsTweaker(float scaleFactor) {
// Windows already scales fonts, scrollbar sizes (etc) according to the system DPI settings.
// This lets us adjust to the REQUESTED scale factor, relative to the CURRENT scale factor
super(scaleFactor / getCurrentScaling());
}
private static float getCurrentScaling() {
int dpi = Toolkit.getDefaultToolkit().getScreenResolution();
return dpi / 96f;
}
public Font modifyFont(Object key, Font original) {
return super.modifyFont(key, original);
}
public Icon modifyIcon(Object key, Icon original) {
return original;
}
}
雨云:
public class NimbusTweaker extends BasicTweaker {
public NimbusTweaker(float scaleFactor) {
super(scaleFactor);
}
public void initialTweaks() {
Font font = uiDefaults.getFont("defaultFont");
if (font != null) {
uiDefaults.put("defaultFont", new FontUIResource(
font.getName(), font.getStyle(), Math.round(font.getSize() * scaleFactor)));
}
}
// Setting "defaultFont" above is sufficient as this will be inherited by all others
public Font modifyFont(Object key, Font original) {
return original;
}
// Scaling Radio or CheckBox button icons leads to really weird artifacts in Nimbus? Disable
public Icon modifyIcon(Object key, Icon original) {
return original;
}
}