特定面板的jdk 9高dpi禁用

使用JDK 9,我的swing应用程序在Windows上运行良好,具有4k highdpi和正常1080p正常dpi。 标签,combobox等都看起来不错,并在4k屏幕上放大。 但我的JPanel也在那里绘制自定义图像。 我可以禁用这个JPanel的缩放来自己处理绘图吗? 我正在使用apache-commons bicubic插值来绘制更高的未缩放分辨率的细节,但是当它从盒子中缩放时,我只需要绘制“正常”尺寸。

亲切的问候

Java 9中的缩放似乎是这样的:您的paint(Component)()方法接收已经缩放的Graphics2D对象 此外,组件大小(例如myJFrame.setSize(),myJPanel.getWidth())将无形地扩展到程序,这意味着当您在200%桌面上说setSize(800,600)时,组件将为1600×1200但getWidth / getHeight将返回800/600。

我可以禁用这个JPanel的缩放来自己处理绘图吗?

要将Graphics对象“重置”为缩放1,请执行以下操作:

final Graphics2D g = (Graphics2D) graphics; final AffineTransform t = g.getTransform(); final double scaling = t.getScaleX(); // Assuming square pixels :P t.setToScale(1, 1); g.setTransform(t); 

要获得正确的尺寸,例如在绘制之前用黑色填充整个背景:

 final int w = (int) Math.round(getWidth() * scaling); 

如果你这样做,你应该在Java 9 Java 8上获得所需的结果。


我刚刚为Java开发人员创建了一个类,他们致力于更自定义的组件设计和/或原始绘图,其中系统的显示缩放应该是已知的,并且通常需要手动缩放。 它应该解决Java 8和Java 9上的所有扩展问题。这里是:

 import javax.swing.*; import java.awt.*; import java.awt.geom.AffineTransform; /** * TL;DR: * 

* Call GUIScaling.initialize() at application start on the Swing thread. *

* If you set your own Component font sizes or border sizes or window sizes, multiply them by * GUIScaling.GUISCALINGFACTOR_COMPONENTSANDFONTS and/or use the helper methods newDimension() and scaleForComponent(). * Works on Java 8 and 9. *

* If you do your own custom graphics and want to have control down to the actual pixel, create an instance of * GUIScalingCustomGraphics to obtain your Graphics2D at scaling 1 and your component's true physical width and height * (Which Java 9 reports differently!), and scale all your graphics using GUIScaling.GUISCALINGFACTOR_CUSTOMGRAPHICS * and/or use the helper method scaleForCustom(). The helper method scaleForRealComponentSize() can transform your mouse * coordinates to the real physical coordinate, which Java 9 reports differently! *

*

*

*

*

*

*

*

*

*

* GUIScaling class v[1, 2017-10-08 11!00 UTC] by dreamspace-president.com *

* This Swing class detects the system's display scaling setting, which is important to make your GUI and custom * graphics scale properly like the user wants it. On a 4K display, for example, you'd probably set 200% in your * system. *

* Not tested with Java less than 8! *

* On Java 8 (and with most but not all (eg no the default) LooksAndFeels), component sizes (eg JButton) and their * font sizes will scale automatically, but if you have a certain border width in mind, or decided for a certain min and * default window size or a certain font size, you have to upscale those on a non-100%-system. With this class, just * multiply the values with GUISCALINGFACTOR_COMPONENTSANDFONTS. Done. newDimension() and scaleForComponent() help with * that. *

* On Java 9, component sizes and their font sizes DO NOT SCALE from the perspective of the application, but in reality * they are scaled: A window set to 800x600 size will really be 1600x1200, but it will still report half this size when * asked. A border of 50 pixels will really be 100 pixels. A Graphics2D object (paint method etc.) will have a scaling * of 2! (Not if you just create a BufferedImage object and do createGraphics(), the scale here will be 1.) So, you * don't have to bother with GUI scaling here at all. YOU CAN STILL USE GUISCALINGFACTOR_COMPONENTSANDFONTS, because * this class will set it to 1 on Java 9. This is detected by indeed checking the scaling of a Graphics2D object. So, * your Java 8 and 9 component/font code will be exactly the same in regards to scaling. *

* CUSTOM GRAPHICS: If you do your own painting and want to insist on true physical pixels (in which case obviously * you'd have to scale your fonts with GUISCALINGFACTOR_CUSTOMGRAPHICS instead of GUISCALINGFACTOR_COMPONENTSANDFONTS), * on Java 9 you have to reset the scaling of the Graphics2D object the paint(Component)() method gives you from 2 to 1, * and (also Java 9) you have to adjust the width/height reported by your component. Both is done by making an instance * of GUIScalingCustomGraphics. You can do this blindly on Java 8 and 9, your code will stay the same. And then, apply * this class' GUISCALINGFACTOR_CUSTOMGRAPHICS to scale everything according to system settings. Or, instead of * insisting on true physical pixels, you could trust Java 9 and not mess with the initial scaling - but then you'd have * to distinguish whether you're dealing with Java 8 or 9, because on 8, you'd still have to scale your custom graphics. * In case you decide for this, use GUISCALINGFACTOR_COMPONENTSANDFONTS for your custom graphics instead of * GUISCALINGFACTOR_CUSTOMGRAPHICS because the former will be ***1*** on Java 9 but will be proper (eg 2.0 for a 200% * system) on Java 8. *

* A weird problem that comes with Java 9: If you use the mouse coordinates as reported by the system (instead of, say, * quasi-fix the physical mouse pointer invisibly at the screen center and make your own pointer based on coordinate * differences), you will have HALF THE USUAL RESOLUTION. On Java 8, a 3840x2160 screen will give you according mouse * coordinates, but on Java 9, you get half these coordinates (if the system is set to scaling 200%). While * scaleForRealComponentSize() helps correct this, a custom drawn mouse pointer will now step in 2 pixel distances, it * can not reach every individual pixel any longer. I wish they had updated the MouseEvent class accordingly with * additional float methods. */ final public class GUIScaling { // INITIAL TOUCHING of this class MUST be on Swing thread! /** * Call this at the start of your application ON THE SWING THREAD. This initializes the class and hence its values. */ public static void initialize() { System.err.println(""); // To make sure an obfuscator doesn't remove this method and its calls. } /** * By calling this, you ALSO initialize the class, so you don't HAVE TO use initialize() in that case (but it really * doesn't matter). And you can indeed set a LookAndFeel of your choice, even though initialization of this class * also sets AND TEMPORARILY USES a LookAndFeel. * * @param intendedLAFIs ANYTHING, but ideally a LookAndFeel name or several. The first value that equalsIgnoreCase * an installed LookAndFeelInfo.getName() will be used. */ public static void setLookAndFeel(final String... intendedLAFIs) { if (intendedLAFIs != null) { final UIManager.LookAndFeelInfo[] installedLAFIs = UIManager.getInstalledLookAndFeels(); LAFILOOP: for (String intendedLAFI : intendedLAFIs) { for (final UIManager.LookAndFeelInfo lafi : UIManager.getInstalledLookAndFeels()) { if (lafi.getName().equalsIgnoreCase(intendedLAFI)) { try { UIManager.setLookAndFeel(lafi.getClassName()); break LAFILOOP; } catch (Exception e) { continue LAFILOOP; } } } } } } /** * Convenience method, compatible with Java 8 and 9. */ public static Dimension newDimension(final int w, final int h) { return new Dimension(scaleForComponent(w), scaleForComponent(h)); } /** * @param x Eg the width of a component, or the size of a border. * @return x scaled by the necessary display scaling factor for components and fonts, compatible with Java 8 and 9. */ public static int scaleForComponent(final double x) { return (int) Math.round(x * GUISCALINGFACTOR_COMPONENTSANDFONTS); } /** * @param x Eg the width of a rectangle being drawn in a paint() or paintComponent() override. * @return x scaled by the necessary display scaling factor for custom graphics, compatible with Java 8 and 9. */ public static int scaleForCustom(final double x) { return (int) Math.round(x * GUISCALINGFACTOR_CUSTOMGRAPHICS); } /** * @param x Eg the width as reported by a component. * @return x scaled so that it represents real physical pixels, compatible with Java 8 and 9. */ public static int scaleForRealComponentSize(final double x) { return (int) Math.round(x * GUISCALINGFACTOR_REALCOMPONENTSIZE); } /** * For Java 9, but can blindly be used in Java 8, too. Ensures that the scaling of a paint(Component)()'s Graphics2D * object is 1. Conveniently does the usual casting, too. *

* Also calculates the physical pixel width/height of the component, which is reported differently on Java 9 if the * display scaling is not 100%. */ final public static class GUIScalingCustomGraphics { final public Component component; // Just for convenience. You can hand the whole instance down your paint call hierarchy. final public int w; // The physical pixel width of the component. final public int h; // dto. height final public Graphics2D g; // Scale will be 1, even on Java 9 with a non-100% display scaling. /** * @param component NOT NULL. The component (eg JPanel or JFrame) whose paint() method you're overriding. * @param graphics NOT NULL. The Graphics argument given to your paint() method. */ public GUIScalingCustomGraphics(final Component component, final Graphics graphics) { this.component = component; w = scaleForRealComponentSize(component.getWidth()); h = scaleForRealComponentSize(component.getHeight()); g = (Graphics2D) graphics; final AffineTransform t = g.getTransform(); t.setToScale(1, 1); g.setTransform(t); } } final private static double JBUTTONFONTSIZE_ON_100PERCENTSCALE_JAVA8_W10_WITH_LOOKANDFEEL_WINDOWSORSYSTEMORXPLATFORMORWINCLASSIC = 11.0; final public static double GUISCALINGFACTOR_SYSTEM; // The scaling set in the system. final public static double GUISCALINGFACTOR_COMPONENTSANDFONTS; // The scaling necessary if you set component/font sizes yourself. final public static double GUISCALINGFACTOR_CUSTOMGRAPHICS; // The scaling necessary if you want your custom graphics, too, to be scaled according to System settings. final public static double GUISCALINGFACTOR_REALCOMPONENTSIZE; // The factor by which getWidth() and such return values have to be multiplied, because Java 9 reports them differently. static { // The last three (Nimbus etc.) DO NOT automatically scale their font sizes with the system's GUI scaling, // so using the font size in those cases to derive the scaling WILL FAIL. // Btw., the JButton font size at 100% Windows 10 system scaling is 11.0 in all cases but the last three. GUIScaling.setLookAndFeel("Windows", UIManager.getSystemLookAndFeelClassName(), UIManager.getCrossPlatformLookAndFeelClassName(), "Windows Classic", "Nimbus", "Metal", "CDE/Motif"); final float jButtonFontSize_on_unknownScale_unknownJava_unknownOS_withLookAndFeelWindows = new JButton().getFont().getSize2D(); // 21.0 on 200% desktop on Java 8 // 11.0 on 100% desktop on Java 8 final Integer[] paintScalingInPercent = new Integer[1]; final JDialog bruteForceJava9ScalingCheck = new JDialog((Frame) null, "", true) { { setLocation(-1000, -1000); // Outamysight! final Runnable fallbackInCaseOlderJavaVersionDoesNotEndUpClosingThisWindow = () -> { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } SwingUtilities.invokeLater(() -> { paintScalingInPercent[0] = 100; dispose(); }); }; final Thread t = new Thread(fallbackInCaseOlderJavaVersionDoesNotEndUpClosingThisWindow); t.setDaemon(true); t.setName("GUI scaling detector fallback thread"); t.start(); } @Override public void paint(final Graphics graphics) { final Graphics2D g = (Graphics2D) graphics; final AffineTransform originalTransform = g.getTransform(); paintScalingInPercent[0] = (int) Math.round(originalTransform.getScaleX() * 100); dispose(); } }; bruteForceJava9ScalingCheck.setVisible(true); // This call blocks until dispose() is reached. if (paintScalingInPercent[0] == null) { throw new Error("Unexpected behavior: Modal dialog did not block!"); } else if (paintScalingInPercent[0] != 100) { GUISCALINGFACTOR_SYSTEM = paintScalingInPercent[0] * 0.01; GUISCALINGFACTOR_COMPONENTSANDFONTS = 1; // Java 9 does everything. The developer's considerations are made unnecessary/harmless by this "1". GUISCALINGFACTOR_CUSTOMGRAPHICS = GUISCALINGFACTOR_SYSTEM; } else { final double factorPreliminary = jButtonFontSize_on_unknownScale_unknownJava_unknownOS_withLookAndFeelWindows / JBUTTONFONTSIZE_ON_100PERCENTSCALE_JAVA8_W10_WITH_LOOKANDFEEL_WINDOWSORSYSTEMORXPLATFORMORWINCLASSIC; // If we just divide the two, we get 1.454545... on a 150% desktop, because the font sizes // chosen by Java are integer values, so we experience a rounding error. // The crappy but probably in most cases nicely working solution is: We round the result to .25 steps! GUISCALINGFACTOR_SYSTEM = Math.round(factorPreliminary * 4) / 4d; GUISCALINGFACTOR_COMPONENTSANDFONTS = GUISCALINGFACTOR_SYSTEM; GUISCALINGFACTOR_CUSTOMGRAPHICS = GUISCALINGFACTOR_SYSTEM; } GUISCALINGFACTOR_REALCOMPONENTSIZE = GUISCALINGFACTOR_CUSTOMGRAPHICS / GUISCALINGFACTOR_COMPONENTSANDFONTS; System.err.println("GUISCALINGFACTOR_SYSTEM = " + GUISCALINGFACTOR_SYSTEM); System.err.println("GUISCALINGFACTOR_COMPONENTSANDFONTS = " + GUISCALINGFACTOR_COMPONENTSANDFONTS); System.err.println("GUISCALINGFACTOR_CUSTOMGRAPHICS = " + GUISCALINGFACTOR_CUSTOMGRAPHICS); System.err.println("GUISCALINGFACTOR_REALCOMPONENTSIZE = " + GUISCALINGFACTOR_REALCOMPONENTSIZE); } }