【问题标题】:java JFrame scaled UI display with virtual drawing space带有虚拟绘图空间的 java JFrame 缩放 UI 显示
【发布时间】:2011-12-06 16:51:59
【问题描述】:

我有一个应用程序,它的图形被认为以 1024x768 显示。

我想让应用程序的大小灵活,而不需要重写所有的绘图代码、位置计算等。

为了实现这一点,我尝试通过以下方式覆盖 JFrame 容器的绘制方法:

@Override
public void paint(Graphics g)
{
    BufferedImage img = new BufferedImage(this.desiredWidth, this.desiredHeight, BufferedImage.TYPE_INT_ARGB);

    Graphics2D gi = (Graphics2D) img.getGraphics();
    gi.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VRGB);
    gi.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
    gi.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);

    super.paint(gi);
    gi.dispose();

    ((Graphics2D) g).drawImage(img, screenScale, null);

}

而 screenScale 是我在构造函数中创建的 AffineTransform 对象,它根据目标大小进行适当的缩放。

现在的问题是:我的子组件被绘制和缩放,但是受到父 JFrame 的限制。因此,如果我的父框架的尺寸为 640x480,那么我添加到它的子层只能在它正在绘制的 1024x768 BufferedImage 的 640x480 部分内绘制。 我猜在某些地方,子组件使用 JFrame 父级的 getPreferredSize,因为子级始终将此值作为界限。所以最后我的缩放策略与孩子们的绘画行为相冲突,因为他们完全忽略了他们为绘画而交付的图形对象的边界。

最后,无论我做什么,当目标尺寸小于我的“虚拟”屏幕尺寸时,我的子层(如果重要,则从 jpanel 派生)被切断。

谁能提供更好的解决方案或提示我如何规避忽略图形边界的奇怪行为?

编辑:更新上述代码的结果,包括未缩放的输出、预期输出和结果输​​出

expected output resulted output

更新:工作测试代码

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import javax.print.attribute.standard.OrientationRequested;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class AffineTransformTest
{

private static TransformingFrame    canvas;
private static JButton button;
private static TestLayer layer;

public static void main(String[] args)
{
    canvas = new TransformingFrame();
    canvas.addMouseWheelListener(new ScaleHandler());

    layer=new TestLayer(canvas.originalSize);
    canvas.getContentPane().add(layer);
    layer.setVisible(true);
    button = new JButton("asdf");
    canvas.setUndecorated(true);
    button.setVisible(true);
    canvas.getContentPane().add(button);


    canvas.pack();



    canvas.setLayout(new BorderLayout());
    canvas.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    canvas.setPreferredSize(canvas.originalSize);
    canvas.setSize(canvas.originalSize);
    canvas.setLayout(null);

    canvas.setVisible(true);
    canvas.validate();
}

@SuppressWarnings("serial")
private static class TransformingFrame extends JFrame
{
    private double  scale;
    private final Dimension originalSize;
    private AffineTransform tx = new AffineTransform();

    TransformingFrame()
    {
        originalSize=new Dimension(800,600);
        scale = 1;
    }

    @Override
    public void paint(Graphics g)
    {
        BufferedImage offscreenBuffer=new BufferedImage(originalSize.width,originalSize.height, BufferedImage.TYPE_INT_ARGB);
        Graphics bufferGraphics=offscreenBuffer.getGraphics();
        super.paint(bufferGraphics);
        bufferGraphics.dispose();

        ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VRGB);
        ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
        g.setColor(Color.black);
        g.fillRect(0, 0, getWidth(), getHeight());
        ((Graphics2D) g).drawImage(offscreenBuffer, tx,null);
    }
    @Override
    public void paintComponents(Graphics g)
    {
        BufferedImage offscreenBuffer=new BufferedImage(originalSize.width,originalSize.height, BufferedImage.TYPE_INT_ARGB);
        Graphics bufferGraphics=offscreenBuffer.getGraphics();
        super.paintComponents(bufferGraphics);
        bufferGraphics.dispose();

        ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VRGB);
        ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
        g.setColor(Color.black);
        g.fillRect(0, 0, getWidth(), getHeight());
        ((Graphics2D) g).drawImage(offscreenBuffer, tx,null);

    }
    @Override
    public void paintAll(Graphics g)
    {
        BufferedImage offscreenBuffer=new BufferedImage(originalSize.width,originalSize.height, BufferedImage.TYPE_INT_ARGB);
        Graphics bufferGraphics=offscreenBuffer.getGraphics();
        super.paintAll(bufferGraphics);
        bufferGraphics.dispose();

        ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VRGB);
        ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
        g.setColor(Color.black);
        g.fillRect(0, 0, getWidth(), getHeight());
        ((Graphics2D) g).drawImage(offscreenBuffer, tx,null);
}

}

@SuppressWarnings("serial")
private static class TestLayer extends JPanel{

public TestLayer(Dimension originalSize)
{
    this.setPreferredSize(originalSize);
    this.setSize(originalSize);
    setOpaque(false);
    setDoubleBuffered(false);
}

@Override
public void paint(Graphics g)
{
    Graphics2D ourGraphics = (Graphics2D) g;
    super.paint(ourGraphics);
    ourGraphics.setColor(Color.green);
    ourGraphics.fillRect(0, 0, getWidth(), getHeight());
    ourGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    ourGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
    ourGraphics.setColor(Color.BLACK);

    ourGraphics.drawRect(50, 50, 50, 50);
    ourGraphics.fillOval(100, 100, 100, 100);
    ourGraphics.drawString("Test Affine Transform", 50, 30);
    ourGraphics.drawString(canvas.tx.toString(), 50, 250);
}

}

private static class ScaleHandler implements MouseWheelListener
{
    public void mouseWheelMoved(MouseWheelEvent e)
    {
        if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL)
        {

            // make it a reasonable amount of zoom
            // .1 gives a nice slow transition
            canvas.scale += (.1 * e.getWheelRotation());
            // don't cross negative threshold.
            // also, setting scale to 0 has bad effects
            canvas.scale = Math.max(0.00001, canvas.scale);
            canvas.tx.setTransform(new AffineTransform());
            canvas.tx.scale(canvas.scale, canvas.scale);
            canvas.setPreferredSize(new Dimension((int)(canvas.originalSize.width*canvas.scale),(int)(canvas.originalSize.height*canvas.scale)));
            canvas.setSize(new Dimension((int)(canvas.originalSize.width*canvas.scale),(int)(canvas.originalSize.height*canvas.scale)));
            canvas.validate();
            canvas.repaint();
        }
    }
}

}

由于某种原因,此代码正在运行(按钮消失除外)..也许我的错误在子层的其他地方..我会去调查一下

好吧,经过几个小时的摆弄,我得出的结论是,子面板在其绘制(图形 g)方法中的绘制限制不允许绘制超过父级的大小。在示例中它可以工作,但在完整的应用程序中则不行。似乎某些设置会强制我的应用程序出现这种行为,而不是演示应用程序。

【问题讨论】:

    标签: java jframe jpanel scale paint


    【解决方案1】:

    因此,如果我的父框架的尺寸为 640x480,那么我添加到其中的子图层只能在 1024x768 的 640x480 部分内绘制

    创建JFrame --> 放在那里JScrollPane --> 到JScrollPane 放:

    1) JPanel 或 JComponent 覆盖 paintComponentn(Graphics g) 而不是 paint(Graphics g)

    2) 你写了关于BufferedImage,那么更好的方法是把BufferedImage 作为Icon 放到JLabel

    【讨论】:

    • 我在代码中不使用任何标签,并且我还以相同的方式覆盖了 Paintcomponents 和 PaintAll,它确实可以部分工作。唯一的问题是我添加到 JPanel 的部分图层在缩放发生之前被切断了。
    • @anubisviech 不要开玩笑,要真正帮助您解决问题,您必须编辑您的问题并在此处以sscce.org 形式发布代码,
    • 感谢您的提示,明天我会尝试发布一个简化的可编译示例
    【解决方案2】:

    正如您所观察到的,组件可以在缩放的图形上下文中呈现,但结果实际上是无用的:UI 委托不知道更改后的几何图形。正如@mKorbel 建议的那样,JScrollPane 是传统的替代方案。

    您还可以查看此game 中使用的方案或此scalable label 中使用的技术。如果您愿意制作自己的组件,则可以采用ScaledView 中显示的方法。

    【讨论】:

    • ScaledView 可能是一个解决方案,因为我不想缩放每一层,而是想绘制整个场景并在绘制完所有内容后在最后一遍进行缩放。
    【解决方案3】:

    在询问了一些人之后,我的问题得到了完全解决。 解决方案是: 1. 创建一个新的类,您可以在其中绘制并进行操作,例如:

    private class ScaledPane extends JPanel
    {
        public ScaledPane(Window parent)
        {
            super();
            setPreferredSize(new Dimension(parent.getDesiredWidth(), parent.getDesiredHeight()));
            setSize(this.getPreferredSize());
        }
    
        @Override
        public void paint(Graphics g)
        {
            Graphics2D g2 = (Graphics2D) g.create(0, 0, getWidth(), getHeight());
            g2.setTransform(screenScale);
            g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); //
            System.out.println(g2.getClip());
            super.paint(g2);
        }
    }
    

    然后将该类的实例设置到您的内容窗格:

    setScreenScale(AffineTransform.getScaleInstance((double) width / (double) desiredWidth, (double) height / (double) desiredHeight));
        setContentPane(new ScaledPane(this));
    

    这样做之后一切都很好,因为窗口的组件使用 contentpanes 绘制方法来使用设置在那里的新图形对象绘制自己

    完成后,我可以将窗口缩放到任何所需的大小,而无需操纵任何孩子的移动公式或位置。

    【讨论】:

      猜你喜欢
      • 2016-07-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-02
      • 2016-10-29
      • 1970-01-01
      相关资源
      最近更新 更多