我正在尝试使用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.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);
}
}
}
}
至于你的为什么不起作用,我不能说,我不能运行这个例子,但是也许这至少会给你一些想法…
所以在我的GUI中,我有一个边框布局的JFrame。在北部和西部有一个菜单栏和一些图形用户界面。在CENTER中,有一个JLabel。我希望它移动到JPanel的中心(水平和垂直)。我如何正确地做到这一点?我尝试了盒子布局和网格布局。一个要求是我不能使用gridpack布局。
这个代码是正确的还是性能更好?
我正在尝试使用ARCore在屏幕上不点击的情况下显示对象。在Google提供的ARCore Sceneform的基本示例中,您需要在屏幕检测到表面后点击屏幕。 我想实现这一点,AR在不点击屏幕的情况下显示对象。 我试着在不点击屏幕的情况下进行显示。 如果有人知道怎么做,请帮助我。提前感谢
在Java中,如何在不使用条件或三元运算符的情况下确定一个数字是奇数还是偶数? 这个问题是我的老师提出的。他还给了我一个提示,使用位运算符是可能的。
问题内容: 我正在努力围绕其中心绘制旋转的位图,并在不调整位图大小的情况下进行旋转。我正在通过游戏线程将所有子画面绘制到屏幕上,因此我正在寻找一种包含原始位图而不是画布的解决方案。 提前致谢。 到目前为止,这是我的代码,它围绕其中心旋转一个位图,但对其进行了调整。 更新: 我注意到这并不像听起来那么容易。我的旋转代码不是问题。位图旋转,但是dst rect也必须根据旋转角度增加/减少,否则bima