如何在不移动中心的情况下放大到 JPanel:数学或 Swing? [英] How to zoom in to JPanel without moving the center: math or Swing?

查看:20
本文介绍了如何在不移动中心的情况下放大到 JPanel:数学或 Swing?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 Swing 制作可缩放的地图.地图是 JScrollPane 中的 JPanel.放大时,地图会改变大小,paint() 在不同的位置绘制元素.这一切都很好.

I am trying to make a zoomable map with Swing. The map is a JPanel in a JScrollPane. When zoomed in, the map changes size and paint() paints the elements in a different position. This all works great.

然而,ScrollPane 在增加图像大小时并没有改变视口,因此放大总是将我正在看的元素移出屏幕.我试图用 scrollRectToVisible() 解决这个问题,但我没有设法获得矩形的正确坐标,要么是因为我在做几何上失败了,要么是因为我不了解 Swing嗯.

However, the ScrollPane didn't change the viewport while increasing the image size, so that zooming in always moved the elements I was looking at out of the screen. I tried to solve this with scrollRectToVisible(), but I don't manage to get the right coordinates for the rectangle, either because I fail at doing the geometry or because I don't understand Swing all that well.

这是我所拥有的:

public class MapPanel extends JPanel {
    [...]

public void setZoom(double zoom) {
    // get the current viewport rectangle and its center in the scaled coordinate system
    JViewport vp = (JViewport) this.getParent();
    Rectangle rect = vp.getViewRect(); 
    Point middle = getMiddle(rect); 
    Dimension dim = rect.getSize();

    // zoom in
    scaler.setZoom(zoom);
    setPreferredSize(scaler.transform(dim));    
    this.revalidate();  

// calculate the full size of the scaled coordinate system  
    Dimension fullDim = scaler.transform(dim); 
    // calculate the non-scaled center of the viewport
    Point nMiddle = new Point((int) ((double) (middle.x)/fullDim.width*dim.width),(int) ((double) (middle.y)/fullDim.height*dim.height));

    // this should do the trick, but is always a bit off towards the origin
    scrollRectToVisible(getRectangleAroundPoint(nMiddle)); 

    // the below alternative always zooms in perfectly to the center of the map 
    // scrollRectToVisible(getRectangleAroundPoint(new Point(400,300)));
}

private Rectangle getRectangleAroundPoint(Point p){
    Point newP = scaler.transform(p);
    Dimension d = railMap.getDimension();
    Point corner = new Point(newP.x-d.width/2,newP.y-d.height/2);
    return new Rectangle(corner,d);
}

private Point getMiddle(Rectangle r){
    return new Point(r.x+r.width/2,r.y+r.height/2);
}
}

这是 Scaler 类(我认为它没有什么特别令人惊讶的地方):

And here's the Scaler class (which does nothing very surprising, I think):

public class Scaler {
    private double zoom = 1;

public void setZoom(double zoom) {
    this.zoom = zoom;
}


public Point transform(Point2D p){
    return new Point((int) (p.getX()*zoom), (int) (p.getY()*zoom));
}


public Dimension transform(Dimension d){
    return new Dimension((int) (d.width*zoom), (int) (d.height*zoom));
}

}

谁能告诉我哪里出了问题?在我看来,我对地图的当前中心进行了有效计算,并且使用固定缩放点确实有效...

Who can tell me where things are going wrong? It seems to me I did a valid calculation of the current center of the map, and with a fixed zoom point it does work...

所以这里的难点是基于旧的视口矩形创建新的视口矩形.

so the hard thing here is to create the new viewport rectangle based on the old viewport rectangle.

推荐答案

我刚刚做了一个非常简单的例子,它基本上试图将滚动窗格保持在所提供图像的中间位置

I just did this really quick example, which basically tries to keep the scroll pane center around the middle of the supplied image

public class TestZooming {

    public static void main(String[] args) {
        new TestZooming();
    }

    public TestZooming() {

        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {

                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException classNotFoundException) {
                } catch (InstantiationException instantiationException) {
                } catch (IllegalAccessException illegalAccessException) {
                } catch (UnsupportedLookAndFeelException unsupportedLookAndFeelException) {
                }

                JFrame frame = new JFrame();
                frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setSize(400, 400);
                frame.setLocationRelativeTo(null);
                frame.setLayout(new BorderLayout());
                final ZoomPane pane = new ZoomPane();
                frame.add(new JScrollPane(pane));
                frame.setVisible(true);

                SwingUtilities.invokeLater(new Runnable() {

                    @Override
                    public void run() {
                        pane.centerInViewport();
                    }

                });

            }
        });

    }

    protected class ZoomPane extends JPanel {

        private Image background;
        private Image scaled;
        private float zoom = 1f;

        private Dimension scaledSize;
        private JViewport con;

        public ZoomPane() {

            try {
                background = ImageIO.read(new File("..."));
                scaled = background;
                scaledSize = new Dimension(background.getWidth(this), background.getHeight(this));
            } catch (IOException ex) {
                ex.printStackTrace();
            }

            InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
            ActionMap am = getActionMap();

            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, 0), "plus");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, InputEvent.SHIFT_DOWN_MASK), "plus");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, 0), "minus");

            am.put("plus", new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    setZoom(getZoom() + 0.1f);
                }
            });
            am.put("minus", new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    setZoom(getZoom() - 0.1f);
                }
            });

            setFocusable(true);
            requestFocusInWindow();

        }

        @Override
        public void addNotify() {

            super.addNotify();

        }

        public float getZoom() {
            return zoom;
        }

        public void setZoom(float value) {
            if (zoom != value) {
                zoom = value;

                if (zoom < 0) {
                    zoom = 0f;
                }

                int width = (int) Math.floor(background.getWidth(this) * zoom);
                int height = (int) Math.floor(background.getHeight(this) * zoom);
                scaled = background.getScaledInstance(width, height, Image.SCALE_SMOOTH);
                scaledSize = new Dimension(width, height);

                if (getParent() instanceof JViewport) {

                    int centerX = width / 2;
                    int centerY = height / 2;

                    JViewport parent = (JViewport) getParent();
                    Rectangle viewRect = parent.getViewRect();
                    viewRect.x = centerX - (viewRect.width / 2);
                    viewRect.y = centerY - (viewRect.height / 2);
                    scrollRectToVisible(viewRect);
                }

                invalidate();
                repaint();

            }
        }

        @Override
        public Dimension getPreferredSize() {

            return scaledSize;

        }

        @Override
        protected void paintComponent(Graphics g) {

            super.paintComponent(g);

            if (scaled != null) {

                g.drawImage(scaled, 0, 0, this);

            }

        }

        protected void centerInViewport() {

            Container container = getParent();
            if (container instanceof JViewport) {

                JViewport port = (JViewport) container;
                Rectangle viewRect = port.getViewRect();

                int width = getWidth();
                int height = getHeight();

                viewRect.x = (width - viewRect.width) / 2;
                viewRect.y = (height - viewRect.height) / 2;

                scrollRectToVisible(viewRect);

            }

        }
    }
}

至于为什么你的不起作用,我不能说,我不能运行这个例子,但也许这至少会给你一些想法......

As to why yours doesn't work, I can't say, I can't run the example, but maybe this will at least give you some ideas...

这篇关于如何在不移动中心的情况下放大到 JPanel:数学或 Swing?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆