开发者

MouseMotionListener in Java Swing, using it with components inside components etc

I am working on a Touch User interface in Swing. While I know this isn't optimal, I am on a short deadline and don't have time to Touch-screen specific GUI packages (if there are any).

I want my users to be able to 'swipe' their finger across the screen, and the view of a special JScrollPane I made moves with it. The code is very simple -

    public class PanScrollPane extends JScrollPane implements MouseMotionListener{  
public PanScrollPane() {
    super();        
    this.addMouseMotionListener(this);      
}
@Override
public void mouseDragged(MouseEvent arg0) {
    System.out.println("Mouse Dragged!");       
}
@Override
public void mouseMoved(MouseEvent arg0) {
    System.out.println("Mouse Moved!");     
}

The problem I'm having is that the JScrollPane is a container for all sorts of JComponents. When I first started working on this, I figured the MouseMovedEvent and MouseDraggedEvent would propagate up the 'GUI tree', untill they encountered a Component with a listener specifically for that event. Now it seems that any component I add to the panScrollPane blocks any of these MouseMotion events, leaving me unable to pan.

    panScrollPane.add(new JButton("This thing blocks any mouse motion events"));

I figured propagating the MouseEvent by hand (adding listeners to every single component and then having them send the event to their parent) would work. This, however, is a very time-intensive undertaking a开发者_如何学JAVAnd as I would rather spend my time working on other things, I was wondering if any of you know any work-around for this problem.

Thanks for reading, and hopefully thanks for answering! :)

edit: To make my intentions clearer. I only want the mousemotion events to be caught by the panPanel, any other event (like MouseClick, MouseRelease) should be processed normally


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.


Getting mouseEvents for a component and all its children is ... tricky to get right. You might consider to rely on stable (and extensively tested :-) code around. The jdk7 way of doing it is to use a JLayer (which internally registers an AWTEventListener as it has all priviledges). For earlier versions, you can use its predecessor JXLayer

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜