【问题标题】:How do I make this panel zoom toward the middle of the panel?如何使此面板向面板中间缩放?
【发布时间】:2013-03-30 04:26:50
【问题描述】:

我有一个 JPanel,这里有代码。当我使用鼠标滚轮滚动时,我希望面板向面板中的中心放大。目前,每当我用鼠标滚轮放大/缩小时,图像左上角的角落都会保持在同一个位置。我很难找到正确的算法。

为了放大图片,代码使用了一个 AffineTransform 对象,该对象根据鼠标滚轮的移动而增加或减少的双精度值来缩放图像。

还增加了复杂性的是图像还可以在面板周围单击和拖动。如果单击并拖动它,则缩放仍必须放大 PANEL 的中心,而不是实际图像的中心。

再一次,缩放应该相对于当前可见区域的中心点进行。也就是说,当缩放发生时,视图中心的点应该保持固定。

这是代码(它是可执行的):

package clientgui;

import java.awt.*;

import javax.imageio.ImageIO;
import javax.swing.*;

import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.net.URL;

import javax.swing.border.TitledBorder;

public class MoveImageExample extends JFrame {
    ShowCanvas canvas;

    public MoveImageExample() throws Exception {
        super();
        Container container = getContentPane();
        canvas = new ShowCanvas(
                "http://cdn.smosh.com/sites/default/files/bloguploads/funny-iphone-5-bruce-lee.jpg");
        container.setPreferredSize(new Dimension(canvas.getWidth(), canvas
                .getHeight()));
        System.out.println("width = " + canvas.getWidth() + " height = "
                + canvas.getHeight());
        container.add(canvas);
        pack();
        setVisible(true);
    }

    public static void main(String arg[]) throws Exception {
        new MoveImageExample();
    }
}

@SuppressWarnings("serial")
class ShowCanvas extends JPanel {
    int imageX = 0, imageY = 0;
    int lastMouseX = 0, lastMouseY = 0;
    int centerX = 225;
    int centerY = 225;
    int canvasWidth = 450;
    int canvasHeight = 450;
    double scaleFactor = 1.0;
    boolean firstMouseDrag = true;
    BufferedImage image;

    public ShowCanvas(String imagePath) throws Exception {
        setBackground(Color.white);
        MouseMotionHandler mouseHandler = new MouseMotionHandler();
        addMouseMotionListener(mouseHandler);
        addMouseListener(mouseHandler);
        addMouseWheelListener(mouseHandler);
        URL url = new URL(imagePath);
        Image rawImage = ImageIO.read(url);
        image = new BufferedImage(rawImage.getWidth(this),
                rawImage.getHeight(this), BufferedImage.TYPE_INT_ARGB);
        setSize(image.getWidth(), image.getHeight());
        Graphics2D g2 = image.createGraphics();
        g2.drawImage(rawImage, imageX, imageY, this);
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2D = (Graphics2D) g;
        g2D.setColor(Color.gray);
        g.fillRect(0, 0, image.getWidth(), image.getHeight());
        AffineTransform transformer = new AffineTransform();
        // translate the image back (using new scale factor)
        transformer.scale(scaleFactor, scaleFactor); // scale by 2x on x and y
                                                        // axes.
        transformer.translate(imageX / scaleFactor, imageY / scaleFactor);
        g2D.drawImage(image, transformer, this);
    }

    class MouseMotionHandler extends MouseMotionAdapter implements
            MouseListener, MouseWheelListener {
        public void mousePressed(MouseEvent e) {
            lastMouseX = e.getX();
            lastMouseY = e.getY();
        }

        public void mouseDragged(MouseEvent e) {
            int xDiff = e.getX() - lastMouseX;
            int yDiff = e.getY() - lastMouseY;
            imageX = imageX + xDiff;
            imageY = imageY + yDiff;
            lastMouseX = e.getX();
            lastMouseY = e.getY();
            repaint();
        }

        public void mouseWheelMoved(MouseWheelEvent e) {
            int notches = e.getWheelRotation();
            scaleFactor = scaleFactor + notches / 10.0;
            if (scaleFactor < 0.5) {
                scaleFactor = 0.5;
            } else if (scaleFactor > 3.0) {
                scaleFactor = 3.0;
            }
            repaint();
        }

        public void mouseReleased(MouseEvent e) {
        }

        public void mouseEntered(MouseEvent e) {
        }

        public void mouseExited(MouseEvent e) {
        }

        public void mouseClicked(MouseEvent e) {
        }
    }
}

【问题讨论】:

  • 基本思路是,当你可以缩放尺寸时,你也需要改变面板的位置
  • 准确更改面板的位置是什么意思?比如,当图像缩放时,图像本身并没有真正移动,但面板在移动?
  • 对不起,应该是“改变图像位置”——我原本以为你也在改变面板的大小......:P

标签: java swing


【解决方案1】:

因此,基本思想是,当您更改比例时,而不是允许从宽度/高度中添加/减去整个更改,您需要将其划分为位置和大小...

public void mouseWheelMoved(MouseWheelEvent e) {
    int notches = e.getWheelRotation();

    // Get the current/old size...    
    double oldWidth = image.getWidth() * scaleFactor;
    double oldHeight = image.getHeight() * scaleFactor;

    scaleFactor = scaleFactor + notches / 10.0;
    if (scaleFactor < 0.5) {
        scaleFactor = 0.5;
    } else if (scaleFactor > 3.0) {
        scaleFactor = 3.0;
    }
    // Get the new size
    double newWidth = image.getWidth() * scaleFactor;
    double newHeight = image.getHeight() * scaleFactor;

    // Calculate the difference (and divide it by 2)
    double difWidth = (oldWidth - newWidth) / 2;
    double difHeight = (oldHeight - newHeight) / 2;

    // Add it to the image position...
    imageX += difWidth;
    imageY += difHeight;

    revalidate();
    repaint();
}

更新了工作示例

好的,基本的想法是您正在处理图像所在的虚拟空间。这个虚拟空间有一个大小(组件大小 x 比例因子)。这使您可以将虚拟空间置于实际空间的中心。

之后,您只需要计算虚拟空间(在真实空间内)的 x/y 偏移量和图像的虚拟位置。

在此示例中,我删除了 AffineTransformation#setLocation 以生成图像的缩放实例,这只是为了更容易放置图像。

import java.awt.*;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.net.URL;

public class MoveImageExample extends JFrame {

    ShowCanvas canvas;

    public MoveImageExample() throws Exception {
        super();
        Container container = getContentPane();
        canvas = new ShowCanvas(
                        "http://cdn.smosh.com/sites/default/files/bloguploads/funny-iphone-5-bruce-lee.jpg");
        container.add(canvas);
        pack();
        setVisible(true);
    }

    public static void main(String arg[]) throws Exception {
        new MoveImageExample();
    }

}

@SuppressWarnings("serial")
final class ShowCanvas extends JPanel {

    int imageX = 0, imageY = 0;
    int lastMouseX = 0, lastMouseY = 0;
    int centerX = 225;
    int centerY = 225;
    int canvasWidth = 450;
    int canvasHeight = 450;
    double scaleFactor = 1.0;
    boolean firstMouseDrag = true;
    BufferedImage image;
    private BufferedImage scaled;

    public ShowCanvas(String imagePath) throws Exception {
        setBackground(Color.white);
        MouseMotionHandler mouseHandler = new MouseMotionHandler();
        addMouseMotionListener(mouseHandler);
        addMouseListener(mouseHandler);
        addMouseWheelListener(mouseHandler);
        URL url = new URL(imagePath);
        Image rawImage = ImageIO.read(url);
        image = new BufferedImage(rawImage.getWidth(this),
                        rawImage.getHeight(this), BufferedImage.TYPE_INT_ARGB);
        setSize(image.getWidth(), image.getHeight());
        Graphics2D g2 = image.createGraphics();
        g2.drawImage(rawImage, imageX, imageY, this);
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension((int) (image.getWidth()), (int) (image.getHeight()));
    }

    protected BufferedImage getScaledInstance() {
        if (scaled == null) {
            int width = (int) (image.getWidth() * scaleFactor);
            int height = (int) (image.getHeight() * scaleFactor);
            scaled = new BufferedImage(width, height, image.getType());
            Graphics2D g2d = scaled.createGraphics();
            AffineTransform transformer = new AffineTransform();
            transformer.scale(scaleFactor, scaleFactor); // scale by 2x on x and y
            g2d.setTransform(transformer);
            g2d.drawImage(image, 0, 0, this);
            g2d.dispose();
        }
        return scaled;
    }

    public Dimension getVirtualSize() {
        return new Dimension(
                        (int)(getWidth() * scaleFactor), 
                        (int)(getHeight() * scaleFactor));
    }

    public Point getVirtualPoint(int x, int y) {
        return new Point(
                        (int)(x * scaleFactor),
                        (int)(y * scaleFactor));
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        Dimension vitualSize = getVirtualSize();
        int xOffset = (getWidth() - vitualSize.width) / 2;
        int yOffset = (getHeight() - vitualSize.height) / 2;

        Graphics2D g2D = (Graphics2D) g.create();
        g2D.setColor(Color.gray);
        g.fillRect(0, 0, image.getWidth(), image.getHeight());

        g2D.setColor(Color.GREEN);
        g2D.drawRect(xOffset, yOffset, vitualSize.width, vitualSize.height);
        g2D.setColor(Color.RED);
        g2D.drawLine(getWidth() / 2, 0, getWidth() / 2, getHeight());
        g2D.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2);

        Point virtualPoint = getVirtualPoint(imageX, imageY);
        System.out.println(virtualPoint);
        g2D.drawImage(getScaledInstance(), virtualPoint.x + xOffset, virtualPoint.y + yOffset, this);
        g2D.dispose();
    }

    class MouseMotionHandler extends MouseMotionAdapter implements
                    MouseListener, MouseWheelListener {

        public void mousePressed(MouseEvent e) {
            lastMouseX = e.getX();
            lastMouseY = e.getY();
        }

        public void mouseDragged(MouseEvent e) {
            int xDiff = e.getX() - lastMouseX;
            int yDiff = e.getY() - lastMouseY;
            imageX = imageX + xDiff;
            imageY = imageY + yDiff;
            lastMouseX = e.getX();
            lastMouseY = e.getY();
            repaint();
        }

        public void mouseWheelMoved(MouseWheelEvent e) {
            scaled = null;
            int notches = e.getWheelRotation();

            scaleFactor = scaleFactor + notches / 10.0;
            if (scaleFactor < 0.5) {
                scaleFactor = 0.5;
            } else if (scaleFactor > 3.0) {
                scaleFactor = 3.0;
            }

            repaint();
        }

        public void mouseReleased(MouseEvent e) {
        }

        public void mouseEntered(MouseEvent e) {
        }

        public void mouseExited(MouseEvent e) {
        }

        public void mouseClicked(MouseEvent e) {
        }

    }

}

【讨论】:

  • 非常非常好的解决方案......这并不是我所需要的。我需要它来放大面板中的中心,而不一定是图像。我真的不知道如何解释,但是如果面板的左上角位于面板的中心点,则面板需要放大该部分。
  • 是的,我刚刚被那部分绊倒了。我不太确定我是否理解它的工作原理
  • 请允许我澄清一下...缩放应该相对于当前可见区域的中心点进行。也就是说,当缩放发生时,视图中心的点应该保持固定。
  • 抱歉,我的笔记本电脑和 ipad 都决定同时耗尽电池。我花了一些时间整夜思考它,并认为我有一个解决方案。我已将调试图形保留在其中,以便您可以看到它正在工作
  • 对不起,拖了这么久,照顾我 10 个月大的孩子,而我妻子生病了,6 天没睡觉 XP
猜你喜欢
  • 2014-09-09
  • 1970-01-01
  • 2011-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-18
  • 1970-01-01
  • 2017-12-22
相关资源
最近更新 更多