如何在不移动中心的情况下放大JPanel:数学还是Swing?

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

但是,ScrollPane在增加图像大小时没有更改视口,因此放大始终会将我正在查看的元素移出屏幕。 我尝试使用scrollRectToVisible()来解决这个问题,但是我没有设法为矩形获得正确的坐标,因为我在做几何体时失败了,或者因为我不太了解Swing。

这是我有的:

 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.xd.width/2,newP.yd.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类(我觉得这没什么大惊讶的):

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

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

编辑:所以这里很难的是根据旧的视口矩形创建新的视口矩形。

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

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

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

解决了它。 好极了。 仍然不确定它实际出错的地方,但是移动原始矩形(感谢@MadProgrammer)而不是创建一个新的矩形并且在缩放器中进行正确的舍入可能已经完成了。

 private Point getViewportCenter() { JViewport vp = (JViewport) this.getParent(); Point p = vp.getViewPosition(); return new Point(p.x+vp.getWidth()/2,p.y+vp.getHeight()/2); } private void setViewportCenter(Point p) { JViewport vp = (JViewport) this.getParent(); Rectangle viewRect = vp.getViewRect(); viewRect.x = px - viewRect.width / 2; viewRect.y = py - viewRect.height / 2; scrollRectToVisible(viewRect); } public void setZoom(double zoom) { // determine unscaled center and dimensions Point oCenter = scaler.inverseTransform(getViewportCenter()); Dimension dim = railMap.getDimension(); // zoom scaler.setZoom(zoom); // fix size and viewport setPreferredSize(scaler.transform(dim)); setViewportCenter(scaler.transform(oCenter)); // should be a transformed point // finish invalidate(); repaint(); }