问题描述
使用JDK 9,我的swing应用程序在Windows上运行良好,具有4k highdpi和
正常1080p正常dpi。标签,组合框等都看起来不错,并且在4k屏幕上放大了
。
但我的JPanel也是如此,我在那里绘制自定义图像。我可以禁用
这个JPanel的缩放比例来自己处理绘图吗?我使用
apache-commons bicubic插值来绘制更高的
未缩放分辨率的更多细节,但是由于它是开箱即用的缩放,我只需要绘制正常尺寸。
With JDK 9 my swing app works well on Windows with 4k highdpi andnormal 1080p normal dpi. Labels, Comboboxes etc. all look nice and arescaled up on the 4k screen.But so is my JPanel where i draw custom images. Can i disablethe scaling for this one JPanel to handle drawing myself? I am usingapache-commons bicubic interpolation to draw more details on the higherunscaled resolution, but as it is scaled out of the box, i just have the "normal" dimensions to draw.
亲切的问候
推荐答案
Java 9中的扩展似乎是这样的:你的paint(Component)()方法接收一个已经缩放的Graphics2D对象。此外,组件大小(例如myJFrame.setSize(),myJPanel.getWidth())被无形地缩放到程序,意味着当你在200%桌面上说setSize(800,600)时,组件将是1600x1200但getWidth / getHeight将返回800/600。
The scaling in Java 9 seems to work like this: Your paint(Component)() methods receive a Graphics2D object which is already scaled. Additionally, the component sizes (e.g. myJFrame.setSize(), myJPanel.getWidth()) are scaled invisibly to the program, meaning that when you say setSize(800,600) on a 200% desktop, the component will be 1600x1200 but getWidth/getHeight will return 800/600.
我可以禁用这个JPanel的缩放以自己处理绘图吗?
要重置你的Graphics对象缩放1,执行此操作:
To "reset" your Graphics object to scaling 1, do this:
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);
要获得正确的尺寸,例如在绘制前用黑色填充整个背景:
To get the correct dimensions, e.g. for filling the whole background with blackness before drawing:
final int w = (int) Math.round(getWidth() * scaling);
如果你这样做,你应该在Java 9上获得所需的结果和 Java 8。
If you do it like this, you should get the desired result on Java 9 and Java 8.
我刚刚为Java开发人员创建了一个类,他们致力于更自定义的组件设计和/或原始绘图,其中应该知道系统的显示缩放并且通常需要手动缩放。它应解决Java 8和Java 9上的所有扩展问题。这是:
I just created a class for Java devs who strive for a more custom Component design and/or raw drawing, where the system's display scaling should be known and manual scaling is often necessary. It should solve all scaling problems on Java 8 and Java 9. Here it is:
import javax.swing.*;
import java.awt.*;
import java.awt.geom.AffineTransform;
/**
* TL;DR:
* <p>
* Call GUIScaling.initialize() at application start on the Swing thread.
* <p>
* 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.
* <p>
* 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!
* <p>
* <p>
* <p>
* <p>
* <p>
* <p>
* <p>
* <p>
* <p>
* <p>
* GUIScaling class v[1, 2017-10-08 11!00 UTC] by dreamspace-president.com
* <p>
* 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.
* <p>
* Not tested with Java less than 8!
* <p>
* On Java 8 (and with most but not all (e.g. no the default) LooksAndFeels), component sizes (e.g. 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.
* <p>
* 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.
* <p>
* 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 (e.g. 2.0 for a 200%
* system) on Java 8.
* <p>
* 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 E.g. 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 E.g. 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 E.g. 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.
* <p>
* 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 (e.g. 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);
}
}
这篇关于特定面板的jdk 9高dpi禁用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!