java 9+ 的 HiDPI 支持,Windows L&F 的 JTable 网格线缩放问题 - 但不是 Nimbus
HiDPI support with java 9+, scalling issue with JTable gridlines with Windows L&F - but not Nimbus
我正在将我的 Swing 应用程序迁移到 Java11 以利用 HiDPI 显示支持。我使用的是三星显示器,分辨率设置为 3840x2160,缩放比例为 125%,Windows 10.
虽然 java9 及更高版本被宣传为正确处理 HiDPI 缩放,但在显示简单的 JTable 时,网格线出现不同的粗细,如下所示:
这是代码:
import javax.swing.*;
public class TestTable {
public static void main(String[] args) {
new TestTable();
}
public TestTable() {
JTable table = new JTable(12,6);
JDialog dialog = new JDialog();
JScrollPane sp = new JScrollPane(table);
table.setShowGrid(true);
table.setRowHeight(25);
dialog.setContentPane(sp);
dialog.setSize(300,300);
dialog.setVisible(true);
dialog.setLocationRelativeTo(null);
}
}
但是,当设置 Nimbus L&F 时,问题就消失了:
import javax.swing.*;
public class TestTable {
public static void main(String[] args) {
try {
for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (Exception e) { }
new TestTable();
}
public TestTable() {
JTable table = new JTable(12,6);
JDialog dialog = new JDialog();
JScrollPane sp = new JScrollPane(table);
table.setShowGrid(true);
table.setRowHeight(25);
dialog.setContentPane(sp);
dialog.setSize(300,300);
dialog.setVisible(true);
dialog.setLocationRelativeTo(null);
}
}
如何使用默认的 Windows L&F 实现同样的效果?
(java 9 和 10 观察到相同的行为)
区别在于两者的外观和感觉如何渲染它们的网格线。
默认外观 MetalLookAndFeel
(和 WindowsLookAndFeel
)基于 BasicLookAndFeel
,它使用 BasicTableUI
class 渲染 JTable
。在 BasicTableUI.paintGrid() it calls such as SwingUtilities2.drawHLine() - 实际上调用 Graphics.fillRect()
就是问题所在。
Nimbus 外观使用 SynthTableUI class。在 SynthTableUI.paintGrid() 中,它最终确实调用了 Graphics.drawLine()
,这显然在缩放下绘制了一条更清晰的线。
正如您所说,这听起来像是 HiDPI 下主要外观的错误。
可以为此创建一个解决方法,尽管它不是特别优雅。
对于正在使用的 Graphics
的自定义版本,如果宽度或高度为 1,则可以覆盖 fillRect()
以使用 drawLine()
。此自定义 Graphics
画的时候可以具体介绍一下table:
JTable table = new JTable(12, 6) {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(new GraphicsWorkaround(g));
}
};
(匿名子class只是为了简洁起见)。
然后 GraphicsWorkaround
class 被写为对传入的真实 g
的包装。这里的 Subclassing DebugGraphics
只是避免在 Graphics
:
中的所有其他方法中编写委托调用的技巧
import java.awt.Graphics;
import javax.swing.DebugGraphics;
public class GraphicsWorkaround extends DebugGraphics {
private final Graphics g;
public GraphicsWorkaround(Graphics g) {
super(g);
this.g = g;
}
@Override
public Graphics create() {
return new GraphicsWorkaround(g.create());
}
@Override
public void fillRect(int x, int y, int width, int height) {
if (width == 1)
g.drawLine(x, y, x, y + height - 1);
else if (height == 1)
g.drawLine(x, y, x + width - 1, y);
else
super.fillRect(x, y, width, height);
}
}
(create()
方法用于处理在 JComponent.paintComponent()
中创建的内部 scratchGraphics
克隆。
这样就可以调用 drawLine()
,在 125% 缩放时看起来好多了。
我正在将我的 Swing 应用程序迁移到 Java11 以利用 HiDPI 显示支持。我使用的是三星显示器,分辨率设置为 3840x2160,缩放比例为 125%,Windows 10.
虽然 java9 及更高版本被宣传为正确处理 HiDPI 缩放,但在显示简单的 JTable 时,网格线出现不同的粗细,如下所示:
这是代码:
import javax.swing.*;
public class TestTable {
public static void main(String[] args) {
new TestTable();
}
public TestTable() {
JTable table = new JTable(12,6);
JDialog dialog = new JDialog();
JScrollPane sp = new JScrollPane(table);
table.setShowGrid(true);
table.setRowHeight(25);
dialog.setContentPane(sp);
dialog.setSize(300,300);
dialog.setVisible(true);
dialog.setLocationRelativeTo(null);
}
}
但是,当设置 Nimbus L&F 时,问题就消失了:
import javax.swing.*;
public class TestTable {
public static void main(String[] args) {
try {
for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (Exception e) { }
new TestTable();
}
public TestTable() {
JTable table = new JTable(12,6);
JDialog dialog = new JDialog();
JScrollPane sp = new JScrollPane(table);
table.setShowGrid(true);
table.setRowHeight(25);
dialog.setContentPane(sp);
dialog.setSize(300,300);
dialog.setVisible(true);
dialog.setLocationRelativeTo(null);
}
}
如何使用默认的 Windows L&F 实现同样的效果?
(java 9 和 10 观察到相同的行为)
区别在于两者的外观和感觉如何渲染它们的网格线。
默认外观 MetalLookAndFeel
(和 WindowsLookAndFeel
)基于 BasicLookAndFeel
,它使用 BasicTableUI
class 渲染 JTable
。在 BasicTableUI.paintGrid() it calls such as SwingUtilities2.drawHLine() - 实际上调用 Graphics.fillRect()
就是问题所在。
Nimbus 外观使用 SynthTableUI class。在 SynthTableUI.paintGrid() 中,它最终确实调用了 Graphics.drawLine()
,这显然在缩放下绘制了一条更清晰的线。
正如您所说,这听起来像是 HiDPI 下主要外观的错误。
可以为此创建一个解决方法,尽管它不是特别优雅。
对于正在使用的 Graphics
的自定义版本,如果宽度或高度为 1,则可以覆盖 fillRect()
以使用 drawLine()
。此自定义 Graphics
画的时候可以具体介绍一下table:
JTable table = new JTable(12, 6) {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(new GraphicsWorkaround(g));
}
};
(匿名子class只是为了简洁起见)。
然后 GraphicsWorkaround
class 被写为对传入的真实 g
的包装。这里的 Subclassing DebugGraphics
只是避免在 Graphics
:
import java.awt.Graphics;
import javax.swing.DebugGraphics;
public class GraphicsWorkaround extends DebugGraphics {
private final Graphics g;
public GraphicsWorkaround(Graphics g) {
super(g);
this.g = g;
}
@Override
public Graphics create() {
return new GraphicsWorkaround(g.create());
}
@Override
public void fillRect(int x, int y, int width, int height) {
if (width == 1)
g.drawLine(x, y, x, y + height - 1);
else if (height == 1)
g.drawLine(x, y, x + width - 1, y);
else
super.fillRect(x, y, width, height);
}
}
(create()
方法用于处理在 JComponent.paintComponent()
中创建的内部 scratchGraphics
克隆。
这样就可以调用 drawLine()
,在 125% 缩放时看起来好多了。