我对Java Swing中mouseDragged
的{{1}}消息的回调速度有疑问。 This post有点相关,但并不完全相同,所以我开始提出自己的问题。
我正在制作一个小型的内部应用程序,不关注商业发行,这基本上是一个数字化的TCG(交易卡游戏)模拟器。对于任何熟悉MtG(Magic the Gathering)的人,你可能已经从这样一个类似的程序中听说过。我正在尝试制作看似this的东西,但不那么花哨。
我的GUI包含一个带有菜单的JFrame,然后是一些包含各种按钮和标签的面板,但我只会查看相关部分来解释我的问题。
本质上,我在左侧使用垂直拆分MouseMotionListener
和JSplitPane
,其中包含JPanel
JScrollPane
,代表JList
随时可以玩你手中的牌。在拆分的右侧,我有JLayeredPane
在DEFAULT_LAYER
(JPanel
的子类中覆盖了draw
函数添加图像的背景图像)和,在PALETTE_LAYER
上方的各个图层上,我通过自定义ArrayList
(CardPanel
的另一个子类显示正在播放的卡片(在JPanel
中收集)说明了一张卡片)。因此,整个JLayeredPane
代表您面前的桌子,并且已经播放了所有牌。
我首先向MouseListener
添加了MouseMotionListener
和JLayeredPane
来接收事件,允许我注册鼠标按下,检查这是否在卡片上方,然后使用鼠标拖动事件移动卡片,最后鼠标释放将其放回原位。这一切都完美无缺,如果我添加日志记录信息,我可以看到mouseDragged
回调函数经常被调用,允许视觉上快速拖动动作而没有滞后。
今天我决定添加功能,允许用户将卡片从手中拖到“桌子”(而不是双击JList
中的卡片),所以我添加了适当的听众。 JList
并填写MousePressed
和MouseReleased
等功能。在鼠标按下时,我检查列表中的哪个卡被点击,我锁定列表,创建一个自定义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等所有这些,但它似乎太麻烦了,它不是我的问题的实际答案为什么一个回调似乎比另一个回调更多,所以请不要告诉我使用它。
答案 0 :(得分:2)
在列表外拖动后,Java开始为列表生成合成鼠标事件,这可能是原因。请参阅javadoc for JComponent#setAutoscrolls(boolean)。
使用全局事件侦听器可能会获得更好的结果,请参阅 http://tips4java.wordpress.com/2009/08/30/global-event-listeners/