Scroll JScrollPane by dragging mouse (Java swing)
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);
}
}
}
I found this (very common) requirement surprisingly hard to solve. This is the stable solution we have had in production for probably over 10 years.
The accepted answer seems very tempting, but has usability glitches once you start to play with it (e.g. try to immediately drag to the lower right and then back, and you should notice that during the backward movement, no moving takes places for a long time).
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.MouseEvent;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.border.MatteBorder;
import javax.swing.event.MouseInputAdapter;
public class Mover extends MouseInputAdapter {
public static void main(String[] args) {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(200, 160);
f.setLocationRelativeTo(null);
f.setLayout(new BorderLayout());
JScrollPane scrollPane = new JScrollPane();
f.add(scrollPane, BorderLayout.CENTER);
JPanel view = new JPanel();
view.add(new JLabel("Some text"));
view.setBorder(new MatteBorder(5, 5, 5, 5, Color.BLUE));
view.setBackground(Color.WHITE);
view.setPreferredSize(new Dimension(230, 200));
new Mover(view);
scrollPane.setViewportView(view);
f.setVisible(true);
}
private JComponent m_view = null;
private Point m_holdPointOnView = null;
public Mover(JComponent view) {
m_view = view;
m_view.addMouseListener(this);
m_view.addMouseMotionListener(this);
}
@Override
public void mousePressed(MouseEvent e) {
m_view.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
m_holdPointOnView = e.getPoint();
}
@Override
public void mouseReleased(MouseEvent e) {
m_view.setCursor(null);
}
@Override
public void mouseDragged(MouseEvent e) {
Point dragEventPoint = e.getPoint();
JViewport viewport = (JViewport) m_view.getParent();
Point viewPos = viewport.getViewPosition();
int maxViewPosX = m_view.getWidth() - viewport.getWidth();
int maxViewPosY = m_view.getHeight() - viewport.getHeight();
if(m_view.getWidth() > viewport.getWidth()) {
viewPos.x -= dragEventPoint.x - m_holdPointOnView.x;
if(viewPos.x < 0) {
viewPos.x = 0;
m_holdPointOnView.x = dragEventPoint.x;
}
if(viewPos.x > maxViewPosX) {
viewPos.x = maxViewPosX;
m_holdPointOnView.x = dragEventPoint.x;
}
}
if(m_view.getHeight() > viewport.getHeight()) {
viewPos.y -= dragEventPoint.y - m_holdPointOnView.y;
if(viewPos.y < 0) {
viewPos.y = 0;
m_holdPointOnView.y = dragEventPoint.y;
}
if(viewPos.y > maxViewPosY) {
viewPos.y = maxViewPosY;
m_holdPointOnView.y = dragEventPoint.y;
}
}
viewport.setViewPosition(viewPos);
}
}