Java Swing中的HiDPI支持多种外观
我希望为一些Swing应用程序添加Hi-DPI支持,但我没有找到一个足以满足我需求的解决方案。我需要支持多个look&feel,因此情况似乎比我发现的其他帖子更复杂(这些帖子倾向于建议“调整UI大小以匹配字体大小”) 一些实验发现,Java Swing中的HiDPI支持多种外观,java,swing,dpi,high-resolution,java-9,Java,Swing,Dpi,High Resolution,Java 9,我希望为一些Swing应用程序添加Hi-DPI支持,但我没有找到一个足以满足我需求的解决方案。我需要支持多个look&feel,因此情况似乎比我发现的其他帖子更复杂(这些帖子倾向于建议“调整UI大小以匹配字体大小”) 一些实验发现,UIManager包含许多指标,可以对这些指标进行调整,使您在应用程序的高DPI友好性方面有一个良好的开端。(该实用程序对于探索这些方面来说是非常宝贵的!)但我发现L&F之间的工作方式似乎完全不同: Windows L&F为您提供了一个良好(不完美)的默认字体大小,
UIManager
包含许多指标,可以对这些指标进行调整,使您在应用程序的高DPI友好性方面有一个良好的开端。(该实用程序对于探索这些方面来说是非常宝贵的!)但我发现L&F之间的工作方式似乎完全不同:
- Windows L&F为您提供了一个良好(不完美)的默认字体大小,并且内置图标(如复选框和窗口图标)的大小适当-但许多其他指标仍然不正常
- 在Metal中,您可以单独更新
中的字体。只需做一点工作,就可以缩放内置的UIManager
s以匹配IconUIResource
- 在Nimbus中,您只需更新一种默认字体,其他字体就会就位。。。但我无法理解如何缩放内置图标并成功渲染组合框、单选按钮等
字体
、图标
、整数
和维度
的默认值
有没有人想出一个好的解决办法
任何人都可以分享一个明确的列表,其中的UIDefaults
需要调整标准L&F
我会很高兴有一个只支持金属和窗户的解决方案
我认为这样的解决方案应该是可重用的&可以为一系列Swing应用程序解决同样的问题。我感到惊讶的是,现在似乎还没有这样的实用工具。(如果不是,请告诉我!)这种方法当然不能解决所有问题(例如,您仍然需要手动将任何调用缩放到setPreferredSize
等。然后,已经支持多个L&Fs的应用程序也应该避免调用它。)尽管如此,我认为它可以让许多应用程序有一个良好的开端
我知道承诺已满,但我不能等那么久——即使在2017年发布之后,我也可能有一段时间无法切换。- 不是答案,只是我对这个问题的见解,请不要删除这个答案,它可以为未来一、两年提供非常丰富的信息
- 我的观点,(我只限于懒惰的Win用户,可能对li/(U)NIX或OSX的真正超级用户的体验很感兴趣)
- 倾向于返回使用NullLayout,尽管UIManager(我认为这仍然是真的)能够处理getPreferredSize 21k x 48k,但似乎LayoutManager不知道如何正确划分这些值,您可以看到,这个问题可以通过使用GridBagLayout看到,4k屏幕的缩放/放大确实存在问题,我认为AWT/Swing GUI在2k监视器上呈现正确
- 对于2k/4k屏幕,您必须覆盖UIManager中的所有键(例如,AFAIK for Nimbus L&F可以直接使用存储在xml文件中的结构)
- 必须创建自己的paintIcon(在AWT/Swing中使用的是简单形状)
- 覆盖边界
- 覆盖鼠标事件
- 覆盖焦点事件
- 在今天的应用中,“平滑GUI”部分也需要这些步骤,从旧的4:3屏幕->低分辨率小屏幕的Think笔记本电脑(高清屏幕)->全高清屏幕->全屏宽屏幕,以2k屏幕结束,注意我从未尝试过使用Nimbus L&F中的尺寸varians,专业申请必须包含测试、检查
- 本机操作系统(字体、边框和大小之间存在差异)
- 通过屏幕缩放,从主显示器获得以像素为单位的屏幕分辨率,例如,特别是全高清屏幕(16:9)和宽全高清屏幕(21:9)
- 如果您有多个显示器且分辨率(像素)不同,则要缓存所有显示器(问题在于有两个相同的显示器,但存在离散设置-GPU的用户设置,或显示器具有活动电视卡-设置可由电视芯片修改)
- 然后可以使用硬编码矩阵为各种大小创建GUI,以符合所有屏幕标准,默认情况下,成功覆盖(父)容器的getPreferredSize,然后LayoutManager将接受getPreferredSize表单硬编码矩阵作为原型,并正确执行自己的作业
- 如果我还记得另一个有趣的事实,虫子,等等。。。等等,我将编辑这篇较长的评论,并附上我的评论
- 这是一个基于我的初始原型的解决方案
我对某些部分不满意,例如修改“整数”和“字体”的规则非常“神奇”。这是一个我希望从其他有更多Swing/L&F经验的人那里听到的地方
我已经把它做成了一个社区维基,所以如果人们更喜欢在上面迭代并添加他们的知识,而不是发布他们自己的解决方案,那么请放心
我从一个可以修改某些ui默认值的界面开始:
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&F使用的功能放在基类中。这似乎可以处理金属而无需进一步细化:
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(image).paintIcon
,并修改了宽度和高度
然后为其他L&F覆盖basictLawer
窗口:
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;
}
}
你关于只有Windows用户的观点正是我