I am making a map editor for a game I am working on. There is a JPanel in the JScrollPane that displays the map to be edited. What I would like to do is make it that when the user is holding down the Spacebar and dragging their mouse in the JPanel, the JScrollPanel will scroll along with the dragging. Here is what I have so far:
panelMapPanel.addMouseMotionListener(new MouseMotionListener(){
@Override
public void mouseDragged(MouseEvent e) {
//Gets difference in distance x and y from last time this listener was called
int deltaX = mouseX - e.getX();
int deltaY = mouseY - e.getY();
mouseX = e.getX();
mouseY = e.getY();
if(spacePressed){
//Scroll the scrollpane according to the distance travelled
scrollPane.getVerticalScrollBar().setValue(scrollPane.getVerticalScrollBar().getValue() + deltaY);
scrollPane.getHorizontalScrollBar().setValue(scrollPane.getHorizontalScrollBar().getValue() + deltaX);
}
}
});
Currently it works but the scrolling is not smooth at all. Moving the mouse a lot at a time is fine but doing small drags makes the scrollpane go berserk.
Any ideas how to improve this?
For those who enjoy a visual to help, here is the editor:
Addition Notes (Edit):
scrollPane.getViewport().setViewPosition(new Point(scrollPane.getViewport().getViewPosition().x + deltaX, scrollPane.getViewport().getViewPosition().y + deltaY));
Okay, that ended up been much simpler then I though it would be...
First, don't mess with the JViewport
, instead, use JComponent#scrollRectToVisible
directly on the component which is acting as the contents of the JScrollPane
, onto which the MouseListener
should be attached.
The following example simply calculates the difference between the point at which the user clicked and the amount they have dragged. It then applies this delta to the JViewport
's viewRect
and uses JComponent#scrollRectToVisible
to update the viewable area, simple :)
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JLabel map;
public TestPane() {
setLayout(new BorderLayout());
try {
map = new JLabel(new ImageIcon(ImageIO.read(new File("c:/treasuremap.jpg"))));
map.setAutoscrolls(true);
add(new JScrollPane(map));
MouseAdapter ma = new MouseAdapter() {
private Point origin;
@Override
public void mousePressed(MouseEvent e) {
origin = new Point(e.getPoint());
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseDragged(MouseEvent e) {
if (origin != null) {
JViewport viewPort = (JViewport) SwingUtilities.getAncestorOfClass(JViewport.class, map);
if (viewPort != null) {
int deltaX = origin.x - e.getX();
int deltaY = origin.y - e.getY();
Rectangle view = viewPort.getViewRect();
view.x += deltaX;
view.y += deltaY;
map.scrollRectToVisible(view);
}
}
}
};
map.addMouseListener(ma);
map.addMouseMotionListener(ma);
} catch (IOException ex) {
ex.printStackTrace();
}
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}