java JFrame масштабированный пользовательский интерфейс с виртуальным пространством рисования - PullRequest
0 голосов
/ 06 декабря 2011

У меня есть приложение с графикой, которая, как считается, отображается в разрешении 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, дочерние слои, которые я добавил к нему, могут рисовать только внутри фракции 640x480 из 1024x768 BufferedImage, на котором он рисует. Я предполагаю, что в некоторых местах дочерние компоненты используют getPreferredSize родительского элемента JFrame, потому что дочерний компонент всегда имеет эти значения в качестве границ. В итоге моя стратегия масштабирования вступает в противоречие с поведением рисования дочерних элементов, поскольку они полностью игнорируют границы графического объекта, который они получают для рисования.

В конце концов, что бы я ни делал, мои дочерние слои (производные от jpanel, если это имеет значение) обрезаются, когда целевой размер меньше моего «виртуального» размера экрана.

Кто-нибудь может предложить лучшее решение или подсказку, как я могу обойти странное поведение, когда графические границы игнорируются?

Редактировать: обновленный результат вышеприведенного кода с немасштабированным выводом, точным выводом и результирующим выводом

ожидаемый результат приведенный вывод

обновление: рабочий тестовый код

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

}

по какой-то причине этот код работает (кроме исчезающей кнопки) .. возможно, моя ошибка находится где-то еще в дочерних слоях .. я пойду исследую, что

Ладно, после нескольких часов возни с этим, я пришел к выводу, что ограничения рисования, которые дочерние панели получают в своем методе paint (Graphics g), не позволяют рисовать больше, чем размер родительского элемента. В примере это работает, но в полном приложении нет. Кажется, что некоторые настройки вызывают такое поведение в моем приложении, но не в демонстрационном приложении.

Ответы [ 3 ]

2 голосов
/ 06 декабря 2011

Так что, если мой родительский фрейм имеет размер 640x480, добавленные к нему дочерние слои могут рисовать только внутри фракции 640x480 из 1024x768

create JFrame -> положить туда JScrollPane -> в JScrollPane положить:

1) JPanel или JComponent с переопределением paintComponentn(Graphics g) не paint(Graphics g)

2) вы написали о BufferedImage, тогда лучший способ поставить BufferedImage как Icon в JLabel

1 голос
/ 06 декабря 2011

Как вы заметили, компонент может быть визуализирован в масштабированном графическом контексте, но результат фактически бесполезен: делегат пользовательского интерфейса не знает об измененной геометрии. Как предполагает @mKorbel, JScrollPane является традиционной альтернативой.

Вы также можете посмотреть схему, использованную в этой игре , или технику, используемую в этой масштабируемой метке . Если вы готовы создавать свои собственные компоненты, вы можете адаптировать подход, показанный в этом ScaledView.

0 голосов
/ 08 декабря 2011

Моя проблема была полностью решена после того, как я спросил некоторых людей об этом. Решение было: 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, чтобы нарисовать себя с новым графическим объектом, который там установлен

После этого я могу масштабировать окно до любого желаемого размера без манипуляций с формулами движения или позициями любого ребенка.

...