3

我有一个使用 java swing 的 java 桌面应用程序,它适用于普通显示器。

但是当来到〜hiDpi〜显示(3200 * 1800)整个应用程序太小了。

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

我已经看到像IntelliJ idea 和eclipse这样的应用程序在这种显示器上运行良好,没有问题。感谢任何帮助。

4

2 回答 2

8

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

以下是我提出的想法的一个例子。它允许您修改UIManagers“字体”属性,对字体大小应用比例。

基本上,它扫描UIManagers UIDefaults,挑选出所有基于“字体”的属性并将它们存储为“基础”。完成后,它会使用这些值,根据比例和原始大小计算字体大小,并更新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 教程,并添加了字体缩放

基本上,当+单击按钮时,它正在运行此代码...

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

基本思想是定义缩放算法,基于“默认”屏幕分辨率1,随着屏幕分辨率/DPI的增加,您可以增加字体缩放以遵循。

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

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

JLayer 缩放

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


        }

    }

}

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

于 2015-12-08T09:48:45.443 回答
0

我没有现成的解决方案,但是:

如果您的元素在 JFrame 中的 JPanel 中(标准方式),请编写您自己的布局管理器,将这个根 Jpanel 缩放到 JFrame 大小的 1/4,并使 JFrame 大小增加四倍(宽 x 2,长 x 2)。

覆盖 JFrame 中的绘画方法,以及一些使用 getGraphics() 的逻辑(您需要调查),确保在将其提供给绘图代码之前在图形上调用 setScale(2,2) 方法,这将使所有内容绘制四次大。

使用 java.awt.EventQueue,编写自己的 EventQueue 来代理所有鼠标事件,并将它们转换为 window(x/2, y/2) 中原始位置的 1/4,然后放开它们,它们应该会触发关于适当元素的事件。

整个想法是让组件保持其原始大小(实际上),但缩放绘图代码和鼠标事件,因此它的外观和工作方式应与低分辨率显示器相同。

于 2015-12-08T07:05:24.007 回答