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% 缩放时看起来好多了。