Поддержка 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 ответ
Разница заключается в том, как эти двое выглядят и чувствуют свои линии сетки.
Внешний вид по умолчанию MetalLookAndFeel
(и WindowsLookAndFeel
) основан на 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%.