【问题标题】:java: end mouseDragged when releasing the mouse outside the source componentjava: 在源组件外释放鼠标时结束 mouseDragged
【发布时间】:2012-02-26 00:57:55
【问题描述】:

我们目前正在修复一个必须在下周完成的项目,所以我们的时间已经不多了(别担心,我只想说我们没有时间折腾我们所有的代码,然后重新开始......)。

我们有一个 JPanel (="page") 和一堆其他的 JPanels (充当图的节点)。用户可以通过单击第一个节点然后单击第二个节点来连接节点,或者通过将一条线从一个节点拖到另一个节点来连接节点。 但是最后一个并没有像我们预期的那样工作:

如果我们将连接线拖出它开始的节点(例如,到页面或任何其他节点),我们用来创建连接线的mouseDragged 事件不会以mouseRelease 结束。如果我在开始拖动的节点上释放鼠标,它就可以正常工作,但我无法创建任何连接。

为了能够绘制到页面的连接,我们正在调度事件,直到它们到达将要处理它们的页面。

有趣的是:一切正常,除了如果我们在mouseDragged() 的代码中添加System.out.println("mouse dragged");,我们就会有无穷无尽的“鼠标拖动”输出链。 连接本身起作用。

所以,我的问题是:如果mouseReleased 发生在mouseDragged 最初开始的组件之外,我该如何结束mouseDragged 事件? 有没有办法中止mouseDragged 事件?

是否有可能/有必要“伪造”一个中断的mouseRelease 事件?

希望有一个解决方案,不会让煮更多的咖啡......

您好, 弗洛尔

编辑: 好的,给你一些代码:

节点和页面实现的接口,让事情变得更简单:

import java.awt.event.MouseEvent;

/**
 * Interface which can be used together with MouseEventListenerAdapter to
 * easily receive mouse events.
 *
 * @author flo
 */
public interface MouseEventsInterface {
    /**
     * Called when a mouse clicked event is received.
     *
     * @param e Triggered mouse event.
     */
    void onMouseClicked(MouseEvent e);
    /**
     * Called when a mouse dragged event is received.
     *
     * @param e Triggered mouse event.
     */
    void onMouseDragged(MouseEvent e);
    [and so on...]
    /**
     * Called when a mouse released event is received.
     *
     * @param e Triggered mouse event.
     */
    void onMouseReleased(MouseEvent e);
}

事件的适配器

/**
 * Class which forwards mouse events to a MouseEventsInterface which have
 * originally been targeted to a MouseInputAdapter.
 *
 * @author flo
 *
 * @param <M> The interface to which events are sent.
 */
public class MouseEventListenerAdapter<M extends MouseEventsInterface> extends MouseInputAdapter {
    /**
     * Interface which receives the events which are forwarded by this class.
     */
    private M mei;

    /**
     * Creates an adapter which forwards all events to the MouseEventsInterface.
     *
     * @param mei The receiver of the forwarded events.
     */
    public MouseEventListenerAdapter(final M mei) {
        this.mei = mei;
    }

    @Override
    public final void mousePressed(final MouseEvent e) {
        this.mei.onMousePressed(e);
    }
    @Override
    public final void mouseClicked(final MouseEvent e) {
        this.mei.onMouseClicked(e);
    }
    @Override
    public final void mouseDragged(final MouseEvent e) {
        this.mei.onMouseDragged(e);
    }
    @Override
    public final void mouseMoved(final MouseEvent e) {
        this.mei.onMouseMoved(e);
    }
    @Override
    public final void mouseReleased(final MouseEvent e) {
        this.mei.onMouseReleased(e);
    }

    /**
     * Returns the receiver of the forwarded {@link MouseEvent MouseEvents}.
     * @return the receiver of the forwarded {@link MouseEvent MouseEvents}.
     */
    protected final M getEventReceiver() {
        return this.mei;
    }
}

我们如何调度事件,如果事件来自节点内的另一个面板,则使用第一部分。 NodeConnectorMouseEvent 是一个常规的 MouseEvent,它能够存储所有发送它的组件...

/**
 * Dispatch mouse event.
 *
 * @param e the e
 */
private void dispatchMouseEvent(final MouseEvent e) {
    if (e instanceof NodeConnectorMouseEvent) {
        ((NodeConnectorMouseEvent) e).addDispatchingComponent(this);
    } else {
        // f.e. moved over IOPort, PortPanel
        e.setSource(this);
    }

    this.getParent().dispatchEvent(e);
}

部分页面代码:

@Override
public final void onMouseDragged(final MouseEvent e) {
    System.out.println("Mouse: dragged");
    if (isDragGestureActive) {
        if (helperConnection != null && e instanceof NodeConnectorMouseEvent) {
            NodeConnectorMouseEvent evt = (NodeConnectorMouseEvent) e;
            if (containsIoPort(evt) != null && weAreDragging) {
                Point absPosition = calcAbsolutePosition(evt);

                // Discard drag events that would jump farther than 15 pixels,
                //but only if we actually weren't dragging
                if (weAreDragging
                        && (Math.abs(absPosition.x - lastDragPosition.x) < 15
                                && Math.abs(absPosition.y - lastDragPosition.y) < 15)) {
                    updateHelperConnection(absPosition);
                }

                lastDragPosition = absPosition;
            }
        } else if (e.getSource() instanceof EditorNode) {
            moveSelectedNodes((EditorNode) e.getSource(), e.getX() - grabPosition.x, e.getY() - grabPosition.y);
        } else {
            return;
        }
    } else {
        return;
    }
}
@Override
public final void onMouseReleased(final MouseEvent e) {
    System.out.println("Mouse: released");
    if (e instanceof NodeConnectorMouseEvent) {
        NodeConnectorMouseEvent evt = (NodeConnectorMouseEvent) e;
        IOPort port = containsIoPort(evt);
        if (port != null) {
            weAreDragging = false;
            lastDragPosition = new Point(0, 0);
            onPortSelected(port);
        }
    } else if (e.getSource() instanceof EditorNode) {
        setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
    } else {
        finishHelperConnection(null);
    }
    isDragGestureActive = false;
}

还有很多,但我想这是有趣的部分。 感谢您的帮助!

【问题讨论】:

  • 它应该按预期结束,无需特殊代码。您确定要同时处理 MouseMotionListener 和 MouseListener 事件吗?没有代码很难说更多......
  • 如果允许,您可以考虑使用 JLayer (jdk7) 或 JXLayer (jdk6) 来装饰节点的父节点 - 您将在自定义 LayerUI 中实现所有鼠标跟踪和线条绘制,即'会大大简化你的逻辑。

标签: java swing drag-and-drop release mouseevent


【解决方案1】:

我不确定我是否理解您的方法,但我想到了两件事:

  • 您可以consume() 一个“事件,这样它就不会被发起它的源以默认方式处理。”

  • 您可以push() 自己的EventQueue 并覆盖dispatchEvent() 以修改任何在前往其最终目的地的途中的事件。有一个相关的例子hereGlobal Event Dispatching 是一个很好的指南。

附录:虽然不适用于眼前的问题,但还有其他方法可以多选节点:

  • GraphPanel 使用鼠标拖动或按住 Shift 单击进行多选,并使用右键单击查看上下文菜单。

  • JGraph 使用 shift-click 进行多项选择; com.mxgraph.examples.swing.GraphEditor 就是一个例子。

  • 这个example 建议如何使用JInternalFrame

【讨论】:

  • consume() 听起来很有希望,我试图玩弄它,但它实际上并没有像我想的那样做:( - 在onMouseDragged(...) 中引入一个布尔终止开关:好吧....它消耗当前事件,但队列中似乎还有更多仍在触发的事件.... - 保存拖动的事件并在onMouseReleased(..) 中消耗它是一样的,它消耗其中一个事件出队列...我不知道如何防止事件被排队或如何从队列中“删除”事件,如果我不再需要它们...
  • 我已经添加了一个指向全局事件调度的链接,这可能有助于删除,我已经详细说明了供参考。
【解决方案2】:

我想知道的一些事情: 1. 在您的页面代码中,您有 lastDragPosition = new Point(0, 0);为什么? 2. 为什么不直接使用mouseExited(MouseEvent e) 方法来模拟拖动中断,只运行一个线程检查是否已经拖动,如果已经完成拖动,连接点

【讨论】:

  • 1.: 我们只是在拖动过程结束时设置一些变量。我们正在显示一条从拖动起点到当前鼠标位置的线,这就是我们需要它们的地方。 2. mouseExit 不起作用,我试过了,但有时如果移动一圈,鼠标似乎比节点快,这当然会导致不必要的拖动中断......
【解决方案3】:

据我观察,当执行拖动时,MouseEvents 不再分派给侦听器。相反,您可以使用DragSourceListener,它有几个有用的方法。你感兴趣的大概是dragDropEnd,在拖拽操作结束时调用,不管成功与否。

【讨论】:

    猜你喜欢
    • 2018-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-28
    • 1970-01-01
    • 2011-10-07
    • 2011-10-08
    相关资源
    最近更新 更多