Java Swing mouseDragged回调速度

时间:2011-12-17 17:16:48

标签: java swing mouse drag

我对Java Swing中mouseDragged的{​​{1}}消息的回调速度有疑问。 This post有点相关,但并不完全相同,所以我开始提出自己的问题。

我正在制作一个小型的内部应用程序,不关注商业发行,这基本上是一个数字化的TCG(交易卡游戏)模拟器。对于任何熟悉MtG(Magic the Gathering)的人,你可能已经从这样一个类似的程序中听说过。我正在尝试制作看似this的东西,但不那么花哨。

我的GUI包含一个带有菜单的JFrame,然后是一些包含各种按钮和标签的面板,但我只会查看相关部分来解释我的问题。

本质上,我在左侧使用垂直拆分MouseMotionListenerJSplitPane,其中包含JPanel JScrollPane,代表JList随时可以玩你手中的牌。在拆分的右侧,我有JLayeredPaneDEFAULT_LAYERJPanel的子类中覆盖了draw函数添加图像的背景图像)和,在PALETTE_LAYER上方的各个图层上,我通过自定义ArrayListCardPanel的另一个子类显示正在播放的卡片(在JPanel中收集)说明了一张卡片)。因此,整个JLayeredPane代表您面前的桌子,并且已经播放了所有牌。

我首先向MouseListener添加了MouseMotionListenerJLayeredPane来接收事件,允许我注册鼠标按下,检查这是否在卡片上方,然后使用鼠标拖动事件移动卡片,最后鼠标释放将其放回原位。这一切都完美无缺,如果我添加日志记录信息,我可以看到mouseDragged回调函数经常被调用,允许视觉上快速拖动动作而没有滞后。

今天我决定添加功能,允许用户将卡片从手中拖到“桌子”(而不是双击JList中的卡片),所以我添加了适当的听众。 JList并填写MousePressedMouseReleased等功能。在鼠标按下时,我检查列表中的哪个卡被点击,我锁定列表,创建一个自定义CardPanel(但不要将它添加到任何地方,我只是分配并启动它!)并设置一个标志。在拖动鼠标时,我检查是否设置了此标志。如果是,我检查光标在哪里。如果它位于JLayeredPane之上的任何位置,我会将CardPanel添加到DRAG_LAYER并设置另一个标记。如果在连续调用拖动鼠标时设置了第二个标志,我不会再次添加面板,而只是更改位置。此功能实际上与我之前鼠标拖动回调中的功能相同。在鼠标发布时,我解锁列表并在CardPanel的正确图层上添加JLayeredPane

一切都按预期工作,所以我很确定我的代码没问题,但只有一个小问题:

将卡从列表拖动到分层窗格(而不是从分层窗格到分层窗格)时,我注意mouseDragged以极低的频率调用JList回调(大约每秒10次),引入一些视觉上令人不安的延迟(相比之下,在第一次拖动时每秒约30次)。

我将添加一些代码片段以澄清我的问题,但我担心添加所有代码以允许您自己运行它会严重过度杀伤。

这篇文章的主要问题是:是否有人知道为什么mouseDragged被一个MouseMotionListener比另一个MouseMotionListener更快? JLayeredPane组件的监听器进行快速连续调用,JList调用的监听器显着变慢。

注意:我正在开发Netbeans,而我正在使用内置的图形Swing Interface Builder。我正在使用JFrame表单作为我的主要类。

public class MyFrame extends JFrame{
    ...
    protected JLayeredPane layeredPane;
    protected JList cardsInHandList;
    ...

    ...
    protected ArrayList<String> cardsInHand;
    ...

    private void attachListeners(){
        layeredPane.addMouseListener(new MouseAdapter(){
            public void MousePressed(MouseEvent e){
                // set a flag, start a drag
            }
            public void MouseReleased(MouseEvent e){
                // unset a flag, stop a drag
            }
        });
        layeredPane.addMouseMotionListener(new MouseMotionAdapter(){
            public void MouseDragged(MouseEvent e){
                // drag the card around
                // gets called a lot!

                // actual code:
                if (e.getButton() == MouseEvent.BUTTON1) {
                if (!dragging) return; // the flag

                int x = e.getX() - 10;
                int y = e.getY() - 10;

                // snap to grid
                x /= GRIDX;
                x *= GRIDX;

                y /= GRIDY;
                y *= GRIDY;

                // redraw the card at its new location
                draggedCard.setLocation(x, y);
            }
            }
        });

        cardsInHandList.addMouseListener(new MouseAdapter(){
            public void MousePressed(MouseEvent e){
                // set a flag, start a drag
            }
            public void MouseReleased(MouseEvent e){
                // unset a flag, stop a drag
            }
        });
        cardsInHandList.addMouseMotionListener(new MouseMotionAdapter(){
            public void MouseDragged(MouseEvent evt){
                // check cursor location, drag if within bounds of layeredPane
                // gets called a whole lot less!! _Why?_

                // actual code:
            if (!draggingFromHand) return; // the flag

            // check location of cursor with own method (contains() didn't work for me)
            if (isCursorAtPointAboveLayeredPane(evt.getLocationOnScreen())) {

                // calculate where and snap to grid
                int x = (int) (evt.getLocationOnScreen().getX() - layeredPane.getLocationOnScreen().getX())-10;
                int y = (int) (evt.getLocationOnScreen().getY() - layeredPane.getLocationOnScreen().getY())-10;

                    // snap to grid
                x /= GRIDX;
                x *= GRIDX;
                y /= GRIDY;
                y *= GRIDY;

                    if(!draggingFromHandCardPanelAdded){
                        layeredPane.add(draggingFromHandCardPanel, JLayeredPane.DRAG_LAYER);
                        draggingFromHandCardPanelAdded = true;
                    } else {
                    draggingFromHandCardPanel.setLocation(x,y);
                    }
            }
        }
        });
    }

我将尝试构建一个简短的可运行示例来重现问题,然后将代码附加到某个地方但是现在我必须进行skoot。

提前致谢

PS :我知道还有另一种方法可以拖动Java,涉及TransferHandlers等所有这些,但它似乎太麻烦了,它不是我的问题的实际答案为什么一个回调似乎比另一个回调更多,所以请不要告诉我使用它。

1 个答案:

答案 0 :(得分:2)

在列表外拖动后,Java开始为列表生成合成鼠标事件,这可能是原因。请参阅javadoc for JComponent#setAutoscrolls(boolean)。

使用全局事件侦听器可能会获得更好的结果,请参阅 http://tips4java.wordpress.com/2009/08/30/global-event-listeners/