MouseMotionListener in Java Swing, using it with components inside components etc
This ad hoc approach leverages the existing JScrollPane
actions that are usually used in key bindings. You'll have to tune N
to your implementation of Scrollable
.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.Timer;
/** @see http://stackoverflow.com/questions/7201509 */
public class ScrollAction extends JPanel {
private static final int TILE = 64;
private static final int DELTA = 16;
public ScrollAction() {
this.setOpaque(false);
this.setFocusable(true);
this.setPreferredSize(new Dimension(50 * TILE, 50 * TILE));
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.lightGray);
int w = this.getWidth() / TILE + 1;
int h = this.getHeight() / TILE + 1;
for (int row = 0; row < h; row++) {
for (int col = 0; col < w; col++) {
if ((row + col) % 2 == 0) {
g.fillRect(col * TILE, row * TILE, TILE, TILE);
}
}
}
}
private void display() {
JFrame f = new JFrame("ScrollAction");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JScrollPane scrollPane = new JScrollPane(this);
final ScrollTimer left = new ScrollTimer(scrollPane, "scrollLeft");
final ScrollTimer right = new ScrollTimer(scrollPane, "scrollRight");
final ScrollTimer up = new ScrollTimer(scrollPane, "scrollUp");
final ScrollTimer down = new ScrollTimer(scrollPane, "scrollDown");
final JViewport viewPort = scrollPane.getViewport();
viewPort.setPreferredSize(new Dimension(5 * TILE, 5 * TILE));
viewPort.addMouseMotionListener(new MouseAdapter() {
@Override
public void mouseMoved(MouseEvent e) {
left.stop();
if (e.getX() < DELTA) {
left.start();
}
right.stop();
if (e.getX() > viewPort.getWidth() - DELTA) {
right.start();
}
up.stop();
if (e.getY() < DELTA) {
up.start();
}
down.stop();
if (e.getY() > viewPort.getHeight() - DELTA) {
down.start();
}
}
});
f.add(scrollPane);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static final class ScrollTimer implements ActionListener {
private static int N = 10;
private static int DELAY = 100;
private String cmd;
private Timer timer;
private Action action;
private JScrollPane scrollPane;
private int count;
public ScrollTimer(JScrollPane scrollPane, String action) {
this.cmd = action;
this.timer = new Timer(DELAY, this);
this.action = scrollPane.getActionMap().get(action);
this.scrollPane = scrollPane;
}
@Override
public void actionPerformed(ActionEvent e) {
if (count++ < N) {
action.actionPerformed(new ActionEvent(scrollPane, 0, cmd));
} else {
timer.stop();
}
}
public void start() {
count = 0;
timer.start();
}
public void stop() {
timer.stop();
count = 0;
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
new ScrollAction().display();
}
});
}
}
How about using a GlassPane? I think its meant to address exactly these types of situations.