使用Java 9+的HiDPI支持,使用Windows L&F的JTable网格线的scalling问题 - 但不是Nimbus

Bas*_*ien 5 java look-and-feel nimbus hidpi

我正在将我的Swing应用程序迁移到Java 11以利用HiDPI显示支持.我正在使用三星显示器,分辨率设置为3840x2160,缩放率为125%,使用Windows 10.

虽然java 9及以上版本被宣传为正确处理HiDPI缩放,但在显示简单的JTable时,网格线显示不同的厚度,如下所示:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,在设置Nimbus L&F时,问题就会消失:

JTable Nimbus

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

如何使用默认的Windows L&F实现相同的功能?

(使用java 9和10观察到相同的行为)

df7*_*899 5

不同之处在于两者的外观和感觉如何渲染它们的网格线。

默认的外观MetalLookAndFeel(和WindowsLookAndFeel)基于BasicLookAndFeel它使用BasicTableUI类来呈现JTable. 在BasicTableUI.paintGrid() 中,它调用了诸如SwingUtilities2.drawHLine() 之类的方法——它实际上调用Graphics.fillRect()了问题所在。

Nimbus 外观使用 SynthTableUI 类。在SynthTableUI.paintGrid() 中,它最终会调用Graphics.drawLine(),这在缩放时清楚地绘制了一条更清晰的线条。

正如您所说,这听起来像是 HiDPI 下主要外观和感觉中的一个错误。


可以为此创建一个解决方法,尽管它不是特别优雅。

使用Graphics正在使用的 的自定义版本,如果宽度或高度为 1,则可以覆盖以替代fillRect()使用drawLine()Graphics可以在绘制表格时专门引入此自定义:

    JTable table = new JTable(12, 6) {
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(new GraphicsWorkaround(g));
        }
    };
Run Code Online (Sandbox Code Playgroud)

(匿名子类只是为了简洁起见)。

然后GraphicsWorkaround该类被编写为g传入的 true 的包装器。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);
    }
}
Run Code Online (Sandbox Code Playgroud)

(该create()方法用于处理在 中scratchGraphics创建的内部克隆JComponent.paintComponent())。

drawLine()毕竟可以被调用,在 125% 的缩放比例下看起来要好得多。