Java Swing应用程序在~HiDpi~计算机中太小了 [英] Java swing application too small in ~HiDpi~ computers

查看:29
本文介绍了Java Swing应用程序在~HiDpi~计算机中太小了的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个使用 java swing 的 java 桌面应用程序,它在普通显示器上运行良好.

I have a java desktop application which uses java swing and it works fine with normal displays.

但是当谈到~hiDpi~显示(3200*1800)时,整个应用程序太小了.

But when come to ~hiDpi~ displays( 3200*1800) whole application is too small.

由于应用程序代码非常庞大且复杂,因此很难重新排列代码以匹配 hi dpi.这个问题有解决方案吗?

As application code is very large and complex, it is difficult to rearrange code to match with hi dpi. Is there a solution to this problem?

我见过像 IntelliJ 想法和 eclipse 这样的应用程序在这种显示器上运行良好,没有问题.感谢您的帮助.

I have seen application like IntelliJ idea and eclipse works fine with this kind of displays without a problem. Appreciate any help.

推荐答案

前一段时间,我的任务是开发一个解决方案,允许用户动态地增加或减少应用程序的字体大小.不用说,我花了很多时间把我的头发扯掉(主要是因为我的前任立即使用了 setPreferredSize 和其他愚蠢的想法,这使得任何解决方案都难以实施)

Some time ago, I was tasked with developing a solution which would allow a user to increase or decrease the font size of the application dynamically. Needless to say, I spent a lot of time tearing my hair out (mostly because my predecessor was instant on using setPreferredSize and other stupid ideas which made any solution a pain to implement)

以下是我提出的想法的一个例子.它允许您修改 UIManager 的字体"属性,将比例应用于字体大小.

The following is an example of the idea I came up with. It allows you to modify the UIManagers "font" properties, applying a scale to the font size.

基本上,它扫描UIManagerUIDefaults,挑选出所有基于字体"的属性并将它们存储为基础".完成后,它使用这些值,根据比例和原始大小计算字体大小,并更新 UIManager

Basically, it scans the UIManagers UIDefaults, picking out all the "font" based attributes and stores these as a "base". Once it's done that, it uses these values, calculates the font size, based on the scale and the original size, and updates the values in the UIManager

import java.awt.Font;
import java.util.HashMap;
import java.util.Map;
import javax.swing.UIManager;
import sun.swing.SwingLazyValue;

public class FontUtilities {

    private static Map<String, Font> originals;

    public static void setFontScale(float scale) {

        if (originals == null) {
            originals = new HashMap<>(25);
            for (Map.Entry entry : UIManager.getDefaults().entrySet()) {
                Object key = entry.getKey();
                if (key.toString().toLowerCase().contains(".font")) {
                    Object value = entry.getValue();
                    Font font = null;
                    if (value instanceof SwingLazyValue) {
                        SwingLazyValue lazy = (SwingLazyValue) entry.getValue();
                        value = lazy.createValue(UIManager.getDefaults());
                    }

                    if (value instanceof Font) {
                        font = (Font) value;
                        originals.put(key.toString(), font);
                    }
                }
            }
        }

        for (Map.Entry<String, Font> entry : originals.entrySet()) {
            String key = entry.getKey();
            Font font = entry.getValue();

            float size = font.getSize();
            size *= scale;

            font = font.deriveFont(Font.PLAIN, size);
            UIManager.put(key, font);
        }
    }

}

示例取自 Swing 教程,并添加了字体缩放

The example is taking from the Swing tutorials, with the addition of the font scaling

基本上,每当点击 + 按钮时,它就会运行此代码...

Basically, when ever the + button is clicked, it's running this code...

scale += 1f;
FontUtilities.setFontScale(scale);
SwingUtilities.updateComponentTreeUI(TextSamplerDemo.this);
updateUI();
revalidate();
repaint();

基本思想是定义缩放算法,基于默认"屏幕分辨率1,随着屏幕分辨率/DPI 的增加,您可以增加字体缩放比例.

The basic idea would be to define a scaling algorithm, based on the "default" screen resolution been 1 and as the screen resolution/DPI increases, you can increase the font scaling to follow.

这有问题.它可能不适用于外观(看着您的灵气),如果您定义自己的字体,它们将不会更新.这也是一种非常好的方式来查看您何时使用 api 做了愚蠢的事情,因为它会对您的布局造成严重破坏

There are problems with this. It might not work on look and feels (looking at you nimbus) and if you define your own fonts they won't be updated. It's also a really great way to see when you've done stupid things with the api, as it will play havoc with your layouts

另一种解决方案是使用 JXLayer/JLayer 来动态缩放整个 UI.

The other solution would be to use JXLayer/JLayer to dynamically scale the UI as a whole.

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.RenderingHints;
import java.util.HashMap;
import java.util.Map;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.jdesktop.jxlayer.JXLayer;
import org.pbjar.jxlayer.demo.TransformUtils;
import org.pbjar.jxlayer.plaf.ext.transform.DefaultTransformModel;

public class TestJLayerZoom {

    public static void main(String[] args) {
        new TestJLayerZoom();
    }

    public TestJLayerZoom() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private JXLayer<JComponent> layer;
        private DefaultTransformModel transformModel;
        private JPanel content;

        public TestPane() {

            content = new JPanel(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridy = 0;

            JLabel label = new JLabel("Hello");
            JTextField field = new JTextField("World", 20);

            content.add(label, gbc);
            content.add(field, gbc);

            gbc.gridy++;
            gbc.gridwidth = GridBagConstraints.REMAINDER;

            final JSlider slider = new JSlider(50, 200);
            slider.addChangeListener(new ChangeListener() {

                @Override
                public void stateChanged(ChangeEvent e) {
                    int value = slider.getValue();
                    double scale = value / 100d;
                    transformModel.setScale(scale);
                }
            });
            content.add(slider, gbc);

            transformModel = new DefaultTransformModel();
            transformModel.setScaleToPreferredSize(true);

            Map<RenderingHints.Key, Object> hints = new HashMap<>();
            //hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            //hints.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
            //hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);

            layer = TransformUtils.createTransformJXLayer(content, transformModel, hints);
            setLayout(new BorderLayout());
            add(layer);


        }

    }

}

这是基于提出的想法 这里.答案中链接了一个额外的库,您需要使用它来完成这项工作

This is based on the idea presented here. There is an additional library linked in the answer which you will need to make this work

这篇关于Java Swing应用程序在~HiDpi~计算机中太小了的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆