Java 9及以上版本的HiDPI支持,使用Windows L&F时JTable网格线存在缩放问题 - 但Nimbus主题没有此问题。

8

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

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

JTable gridlines issue

这是此问题的代码:

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主题时,问题就消失了:

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);
    }
}

我该如何使用默认的Windows L&F实现相同的效果?(在Java 9和10中观察到相同的行为)
1个回答

6
两者之间的区别在于它们如何呈现网格线的外观和感觉。
默认的外观和感觉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));
        }
    };

一个匿名子类只是为了简洁起见。

然后,GraphicsWorkaround类被编写为对传入的真正g的包装器。在这里,子类化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%缩放下看起来更好。

谢谢,我会接受这个答案,因为它清楚地解释了这种行为的根本原因,并提出了一个很好的解决方法。然而,在我的端上(扩展原始示例时),它会产生一些其他问题,涉及字体和颜色,但我相信我可以找到解决方案。我想我会报告这种行为(错误?),看看会发生什么。 - Bastien

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接