当前位置: 首页 > 面试题库 >

特定面板的jdk 9高dpi禁用

武友樵
2023-03-14
问题内容

使用JDK
9,我的swing应用程序可以在4k高dpi和普通1080p普通dpi的Windows上很好地工作。标签,组合框等都看起来不错,并在4k屏幕上放大。但是我在JPanel上绘制自定义图像的地方也是如此。我可以禁用此JPanel的缩放比例以处理自己的绘制吗?我正在使用apache-
commons双三次插值法在较高的未缩放分辨率上绘制更多细节,但是由于它是按比例缩放的,因此我只能绘制“正常”尺寸。

亲切的问候


问题答案:

(编辑:以大约2年的使用后的格式从我的库中粘贴了新版本。注释可能不是最新的,但是现在值得使用生产代码。)

Java 9中的缩放似乎是这样工作的:您的paint(Component)()方法收到一个 已经缩放 的Graphics2D对象
此外,组件的大小(例如myJFrame.setSize(),myJPanel.getWidth())会 根据程序隐式 缩放
这意味着当您在200%的桌面上说setSize(800,600)时,组件将为1600x1200,但getWidth /
getHeight将返回800/600。

我可以禁用此JPanel的缩放比例以处理自己的绘制吗?

要将“图形”对象“重置”为缩放比例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:
 * <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[___3___, 2019-04-23 07!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.print("");
    }


    public static void setLookAndFeelDefault() {
        // 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");
    }


    /**
     * 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 && intendedLAFIs.length > 0) {
            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;
                        }
                    }
                }
            }
        } else {
            throw new IllegalArgumentException("intendedLAFIs is null or empty.");
        }
    }


    /**
     * 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 v E.g. the width of a component, or the size of a border.
     * @return v scaled by the necessary display scaling factor for components and fonts, compatible with Java 8 and 9.
     */
    public static int scaleForComponent(final double v) {

        return (int) Math.round(v * GUISCALINGFACTOR_COMPONENTSANDFONTS);
    }


    /**
     * @param v E.g. the width of a rectangle being drawn in a paint() or paintComponent() override.
     * @return v scaled by the necessary display scaling factor for custom graphics, compatible with Java 8 and 9.
     */
    public static int scaleForCustom(final double v) {

        return (int) Math.round(v * GUISCALINGFACTOR_CUSTOMGRAPHICS);
    }


    /**
     * @param v E.g. the width as reported by a component. (Java 9 on 200% desktop reports e.g. 200, but the physical
     *          size is actually 400. This method returns 400 then.)
     * @return v scaled so that it represents real physical pixels, compatible with Java 8 and 9.
     */
    public static int scaleForRealComponentSize(final double v) {

        return (int) Math.round(v * GUISCALINGFACTOR_REALCOMPONENTSIZE);
    }


    /**
     * @param font A font instance (Or null. Returns null.) whose size has been derived kinda like this: "new
     *             JLabel().getFont().getSize()" So it will look correct when used in components, no matter the current
     *             Java version. ......... WAIT WTF why does that look correct on Java 8 ?!??!?!?!?!?!?!?! Anyway ...
     *             when you want to use THAT font in custom drawing, you'll have a bad time once you get on Java 9.
     *             Because components will have SMALLER font sizes than on Java 8 on a 200% desktop because their
     *             Graphics objects are scaled. But if you use custom drawing, you'll use the class
     *             GUIScalingCustomGraphics below, which reset the scaling to 1. But then the font is too small. THIS
     *             METHOD RETURNS THE SCALED FONT independent of the Java version.
     * @return
     */
    public static Font scaleFontForCustom(final Font font) {

        if (font != null) {
            return font.deriveFont(font.getSize2D() * (float) GUISCALINGFACTOR_REALCOMPONENTSIZE);
        }
        return null;
    }


    /**
     * 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 double guiScalingFactor_manualDrawing = GUISCALINGFACTOR_CUSTOMGRAPHICS;
        final public double guiScalingFactor_fontsThatLookCorrectInComponents = GUISCALINGFACTOR_FONTINCUSTOMGRAPHICSCONTEXT;

        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();
            final double xTrans = t.getTranslateX();
            final double yTrans = t.getTranslateY();
            t.setToScale(1, 1);
            t.translate(xTrans, yTrans);
            g.setTransform(t);
        }


        /**
         * @param graphics NOT NULL. The Graphics argument given to your paint() method.
         */
        public GUIScalingCustomGraphics(final Graphics graphics) {

            component = null;
            w = 0;
            h = 0;

            g = (Graphics2D) graphics;
            final AffineTransform t = g.getTransform();
            t.setToScale(1, 1);
            g.setTransform(t);
        }


        /**
         * @param x E.g. the width of a rectangle to be drawn.
         * @return x scaled so that it represents real physical pixels, compatible with Java 8 and 9.
         */
        public int scale(final double x) {

            return (int) Math.round(x * guiScalingFactor_manualDrawing);
        }


        /**
         * Scales the font and then sets it. Assumption: The given font looked correct in components. In Java 9, it
         * would be TOO SMALL in custom graphics via this class. This method fixes that.
         *
         * @param font NULL = null will be returned and G's font will not change.
         * @return returns the set font for convenience. NULL if your argument was null.
         */
        public Font setFont(final Font font) {

            final Font ret = font == null ? null : font.deriveFont(font.getSize2D() * (float) guiScalingFactor_fontsThatLookCorrectInComponents);
            g.setFont(ret);
            return ret;
        }


        @Override
        public String toString() {

            return "[GUIScalingCustomGraphics" +
                    " guiScalingFactor_manualDrawing=" + guiScalingFactor_manualDrawing +
                    " w=" + w +
                    " h=" + h +
                    " component=" + component +
                    " g=" + g +
                    ']';
        }
    }




    final private static double JBUTTONFONTSIZE_ON_100PERCENTSCALE_JAVA8_W10_WITH_LOOKANDFEEL_WINDOWSORSYSTEMORXPLATFORMORWINCLASSIC = 11.0;
    final public static float JBUTTONFONTSIZE_ON_UNKNOWNSCALE_UNKNOWNJAVA_UNKNOWNOS_WITH_LOOKANDFEEL_WINDOWSORSYSTEMORXPLATFORMORWINCLASSIC;

    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.
    final public static double GUISCALINGFACTOR_FONTINCUSTOMGRAPHICSCONTEXT; // (This is exactly the custom graphics scaling probed by this class.) The factor by which a proper looking font would have to be scaled when used in custom graphics whose scale is 1. (Java 9 pre-scales it to e.g. 2 if Desktop is at 200%, then you reset that with the class above. Then the fonts that look right in the components will be TOO SMALL in the custom graphics. Use this factor / the method above to fix that.)

    static {

        if (!SwingUtilities.isEventDispatchThread()) { // This also makes sure an obfuscator doesn't remove this method and its calls.
            throw new IllegalStateException("Must be initialized on Swing thread!");
        }

        System.err.println("Initializing GUI scaling ...");

        GUIScaling.setLookAndFeelDefault();

        JBUTTONFONTSIZE_ON_UNKNOWNSCALE_UNKNOWNJAVA_UNKNOWNOS_WITH_LOOKANDFEEL_WINDOWSORSYSTEMORXPLATFORMORWINCLASSIC =
                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!
                setLocation(100,
                            100); // else you might have compatibility problems (see stackoverflow where I posted this class)

                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) {

            // Must be Java 9 (or newer?).

            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;
            GUISCALINGFACTOR_FONTINCUSTOMGRAPHICSCONTEXT = GUISCALINGFACTOR_SYSTEM;

        } else {

            // Either Java 8 (or older?) or scaling IS just at the normal 1 (100).

            final double factorPreliminary = JBUTTONFONTSIZE_ON_UNKNOWNSCALE_UNKNOWNJAVA_UNKNOWNOS_WITH_LOOKANDFEEL_WINDOWSORSYSTEMORXPLATFORMORWINCLASSIC / 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_FONTINCUSTOMGRAPHICSCONTEXT = 1; // No font scaling, the fonts used will look correct in custom scaling without extra treatment.
        }

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

        System.err.println("... done.");

    }

}


 类似资料:
  • 问题内容: 我正在尝试使用参数运行Java应用程序,但没有任何反应。 我期望用户界面模糊,但图标和字体的大小正常,看来此标志不起作用。 我在Windows 8.1上使用JDK 1.8.0_45。 我发现了这个错误https://bugs.openjdk.java.net/browse/JDK-8080153,但我不知道如何解决它。 问题答案: 创建一个Windows注册表编辑新的DWORD 按Wi

  • 我正在寻找禁用特定页面的手势。我遇到了许多帖子,建议将MasterDetailPage的设置为false以禁用它。这有效,但出于某种原因并不总是如此。 目前我只想在登录页面禁用手势,所以在身份验证时我启用手势,在出现时我禁用。 我遇到的问题是,如果用户切换应用程序并返回并退出,手势仍然启用。这让我认为正在触发不同的MasterDetailePage上下文。 有没有人遇到过这个问题,或者对我有什么指

  • 我有一个使用系统外观的Java Swing程序: 问题是在高DPI系统中,框架中的字体太小了。我如何使框架上的文本可读而不必更改所有框架的字体?我的程序是使用Java6编写的,有太多的框架需要修改。

  • 我试图用Dsun运行一个Java应用程序。java2d。dpiaware=false,但什么也没有发生。 我希望有一个模糊的UI,但正常大小的图标和字体,似乎这个标志不起作用。 我在Windows 8.1上使用JDK 1.8.0\U 45。 我发现了这个bughttps://bugs.openjdk.java.net/browse/JDK-8080153但我不知道如何解决这个问题。

  • 问题内容: 我有一个模板,显示作者可以编辑/删除的各种条目。用户可以单击“删除”来删除其帖子 删除后,用户将被重定向到条目页面,但该项目仍然存在,因此需要再次重新加载页面以显示删除效果。如果我禁用缓存,问题就会消失,但是我真的很想在所有其他页面中都拥有缓存… 添加这些标签无效,我认为我的浏览器只会忽略它们 我启用缓存槽: 有什么方法可以针对特定页面禁用它吗? 编辑 如建议,我尝试使用包装器: 并将

  • 我如何在Drupal 9中禁用特定页面的缓存,我找到了一个解决方案,但它用于禁用所有页面的缓存,解决方案是:在settings.yml中添加此代码: 这是工作,但我只想禁用特定页面的缓存。谢谢。