Поддержка HiDPI с java 9+, устранение проблемы с линиями сетки JTable с Windows L&F - но не с Nimbus

Я перевожу свое приложение Swing на Java 11, чтобы воспользоваться поддержкой отображения HiDPI. Я использую монитор Samsung с разрешением 3840x2160, масштабирование на 125%, с Windows 10.

Хотя java 9 и выше объявлены как должным образом обрабатывающие масштабирование 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)

1 ответ

Решение

Разница заключается в том, как эти двое выглядят и чувствуют свои линии сетки.

Внешний вид по умолчанию MetalLookAndFeelWindowsLookAndFeel) основан на BasicLookAndFeel который использует BasicTableUI класс для рендеринга JTable, В BasicTableUI.paintGrid() это вызывает, например, SwingUtilities2.drawHLine () - который на самом деле вызывает Graphics.fillRect() в чем проблема.

Внешний вид Nimbus использует класс SynthTableUI. В SynthTableUI.paintGrid() это в конечном счете вызывает Graphics.drawLine(), которая четко рисует более чистую линию при масштабировании.

Как вы говорите, это звучит как ошибка в основном виде и ощущается под HiDPI.


Для этого можно создать обходной путь, хотя он не особенно элегантен.

С пользовательской версией Graphics что используется, можно переопределить fillRect() использовать drawLine() вместо этого, если ширина или высота равна 1. Это обычай 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);
    }
}

(The create() метод есть, чтобы справиться с внутренним scratchGraphics клон создан в JComponent.paintComponent()).

Это затем позволяет drawLine() быть вызванным в конце концов, который выглядел намного лучше при масштабировании 125%.

Другие вопросы по тегам