在一个面板内进行缩放

17

我有一个面板,其中一些2D对象正在移动。我已经根据需要重写了paintComponent()。现在我想能够缩放该区域。缩放时,将出现滚动条,可以通过滚动查看整个区域。在缩放时,2D对象应相应地增加或减小。哪种Swing组件或组件的组合可帮助实现此目标?

2个回答

18

最简单的方法是修改您的面板并引入一个指示您缩放级别的双倍数。这个双倍数将指示您的比例尺,其中1为正常大小,更高的数字表示缩放。您可以在paintComponent中使用该双倍数和Graphics2D

例如:

Graphics2D g2 = (Graphics2D) g;
int w = // real width of canvas
int h = // real height of canvas
// Translate used to make sure scale is centered
g2.translate(w/2, h/2);
g2.scale(scale, scale);
g2.translate(-w/2, -h/2);

为了进行滚动,将您的面板放入JScrollPane中,并结合一个使用缩放比例的getPreferredSize。JScrollPane使用放置在其中的组件的首选大小。如果首选大小超过自己的大小,它将显示滚动条。

如果更改面板的首选大小,使其返回的宽度和高度被缩放,那么您就可以了。基本上,您只需返回类似于以下内容的东西:

return new Dimension(w * scale, h * scale)

1
谢谢。您能解释一下“并将其与使用缩放比例的getPreferredSize相结合”的含义吗? - aps
好的。首先,我需要一个包含paintComponent()方法的JPanel。在paintComponent()中,我会使用graphics2D和double来保持缩放级别。我还需要将double值与某些事件处理机制(如鼠标滚轮或缩放按钮的按下)相关联。随着缩放值的增加或减少,面板的getPreferredSize将返回不同的值给JScrollPane,它将相应地完成其工作。还有其他需要注意的吗?非常感谢。 - aps

9
我知道这个问题很老,但我想发表我的解决方案,以防将来有人能够使用它。
因此,我创建了一个扩展JPanel的类,该类实现了MouseWheelListener以便检测用户滚动鼠标。我的类还监听拖动以便在用户单击和拖动时移动内容。
代码说明
首先,在构造函数中,您必须将此设置为MouseWheelListener。
 addMouseWheelListener(this);

为了实现缩放,我使用了一个布尔值zoomer(用于指示用户何时使用鼠标滚轮),以及两个双精度浮点数zoomFactor(用于保存对象大小乘以的当前因子)和prevZoomFactor(用于前一次缩放因子)。

private double zoomFactor = 1;
private double prevZoomFactor = 1;
private boolean zoomer;

我还重写了JPanel的paint()方法,在此方法中(在绘制任何内容之前),如果用户缩放(zoomer=true), 我将图形按照zoomFactor进行缩放。代码:

@Override
public void paint(Graphics g) {
    super.paint(g);
    Graphics2D g2 = (Graphics2D) g;
    if (zoomer) {
        AffineTransform at = new AffineTransform();
        at.scale(zoomFactor, zoomFactor);
        prevZoomFactor = zoomFactor;
        g2.transform(at);
        zoomer = false;
    }
    // All drawings go here
}

最后,我重写了MouseWheelListener的mouseWheelMoved方法,在该方法中,如果用户向上滚动,则增加zoomFactor;如果用户向下滚动,则减少zoomFactor。 代码如下:

@Override
public void mouseWheelMoved(MouseWheelEvent e) {
    zoomer = true;
    //Zoom in
    if (e.getWheelRotation() < 0) {
        zoomFactor *= 1.1;
        repaint();
    }
    //Zoom out
    if (e.getWheelRotation() > 0) {
        zoomFactor /= 1.1;
        repaint();
    }
}

示例

如果你想要使用拖动功能,并且希望根据鼠标位置进行缩放,你可以使用以下类,在构造函数中传入一个BufferedImage参数以便在屏幕上显示内容。

我还在GitHub上上传了一个名为Zoomable-Java-Panel的项目,其中有一个可运行的示例,展示了我上述所说的内容,您可以测试并了解如何将其应用到项目中。

package zoomable.panel;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.MouseInfo;
import java.awt.Point;
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.swing.JPanel;

/**
 *
 * @author Thanasis1101
 * @version 1.0
 */
public class MainPanel extends JPanel implements MouseWheelListener, MouseListener, MouseMotionListener {

    private final BufferedImage image;

    private double zoomFactor = 1;
    private double prevZoomFactor = 1;
    private boolean zoomer;
    private boolean dragger;
    private boolean released;
    private double xOffset = 0;
    private double yOffset = 0;
    private int xDiff;
    private int yDiff;
    private Point startPoint;

    public MainPanel(BufferedImage image) {

        this.image = image;
        initComponent();

    }

    private void initComponent() {
        addMouseWheelListener(this);
        addMouseMotionListener(this);
        addMouseListener(this);
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);

        Graphics2D g2 = (Graphics2D) g;

        if (zoomer) {
            AffineTransform at = new AffineTransform();

            double xRel = MouseInfo.getPointerInfo().getLocation().getX() - getLocationOnScreen().getX();
            double yRel = MouseInfo.getPointerInfo().getLocation().getY() - getLocationOnScreen().getY();

            double zoomDiv = zoomFactor / prevZoomFactor;

            xOffset = (zoomDiv) * (xOffset) + (1 - zoomDiv) * xRel;
            yOffset = (zoomDiv) * (yOffset) + (1 - zoomDiv) * yRel;

            at.translate(xOffset, yOffset);
            at.scale(zoomFactor, zoomFactor);
            prevZoomFactor = zoomFactor;
            g2.transform(at);
            zoomer = false;
        }

        if (dragger) {
            AffineTransform at = new AffineTransform();
            at.translate(xOffset + xDiff, yOffset + yDiff);
            at.scale(zoomFactor, zoomFactor);
            g2.transform(at);

            if (released) {
                xOffset += xDiff;
                yOffset += yDiff;
                dragger = false;
            }

        }

        // All drawings go here

        g2.drawImage(image, 0, 0, this);

    }

    @Override
    public void mouseWheelMoved(MouseWheelEvent e) {

        zoomer = true;

        //Zoom in
        if (e.getWheelRotation() < 0) {
            zoomFactor *= 1.1;
            repaint();
        }
        //Zoom out
        if (e.getWheelRotation() > 0) {
            zoomFactor /= 1.1;
            repaint();
        }
    }

    @Override
    public void mouseDragged(MouseEvent e) {
        Point curPoint = e.getLocationOnScreen();
        xDiff = curPoint.x - startPoint.x;
        yDiff = curPoint.y - startPoint.y;

        dragger = true;
        repaint();

    }

    @Override
    public void mouseMoved(MouseEvent e) {
    }

    @Override
    public void mouseClicked(MouseEvent e) {

    }

    @Override
    public void mousePressed(MouseEvent e) {
        released = false;
        startPoint = MouseInfo.getPointerInfo().getLocation();
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        released = true;
        repaint();
    }

    @Override
    public void mouseEntered(MouseEvent e) {

    }

    @Override
    public void mouseExited(MouseEvent e) {

    }

}

你不应该需要覆盖 paint() 方法。而是应该覆盖 paintComponent() 方法。在大多数情况下,效果是相同的。但当你有一个不透明的面板或向面板添加了小部件等其他情况时,会出现问题。 - Mark Jeronimus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接