我正在尝试使用Swing制作可缩放的地图.该地图是JScrollPane中的JPanel.放大时,地图会更改大小,而paint()会将元素绘制在不同的位置.一切都很好. 但是,ScrollPane在增加图像大小时不会更改视口
但是,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.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类(我认为这没什么大惊讶的):
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); } } } }
至于你为什么不工作,我不能说,我不能运行这个例子,但也许这至少会给你一些想法……