【问题标题】:Component stops firing mouse drag events when added to a window组件添加到窗口时停止触发鼠标拖动事件
【发布时间】:2020-12-25 22:37:09
【问题描述】:

我有一个可以拖动组件的容器。我遇到的问题是每当我拿起一个组件并将其自动添加到窗口时,即使鼠标仍在手柄上拖动,该组件也会停止触发拖动事件。然后下次我单击并拖动手柄(现在在浮动窗口中)时,它会继续拖动。下面是一些基本代码:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.Box.Filler;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import javax.swing.border.LineBorder;

/**
 *
 */
@SuppressWarnings("serial")
public class DraggableDemo extends JPanel {

    private class DraggablePanel extends JPanel {

        private class MyMouseAdapter extends MouseAdapter {
            private DraggablePanel floater;
            private Point dragOffset;

            @Override
            public void mouseDragged(MouseEvent e) {
                if (floater == null) {
                    startDragging(e);
                }

                Point transformedEventPoint = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), owner);
                updateWindowLocation(transformedEventPoint);

            }

            private void startDragging(MouseEvent e) {
                Component component = e.getComponent();
                floater = (DraggablePanel) SwingUtilities.getAncestorOfClass(DraggablePanel.class, component);
                Point floaterAbsoluteLocation = SwingUtilities.convertPoint(floater.getParent(), floater.getLocation(), owner);
                Point transformedEventPoint = SwingUtilities.convertPoint(component, e.getPoint(), owner);
                // designate a drag offset so the component's corner doesn't teleport to mouse location
                dragOffset = new Point(transformedEventPoint.x - floaterAbsoluteLocation.x, transformedEventPoint.y - floaterAbsoluteLocation.y);

                swapComponents(getFiller(), floater);

                // place the floating component in a window
                window.add(floater);
                window.pack();
                floater.setBorder(new LineBorder(Color.YELLOW));
                updateWindowLocation(transformedEventPoint);
                window.setVisible(true);
            }

            private void updateWindowLocation(Point point) {
                Point p = new Point(point.x - dragOffset.x, point.y - dragOffset.y);
                SwingUtilities.convertPointToScreen(p, owner);
                window.setLocation(p);
            }

            private JComponent getFiller() {
                Dimension dimension = floater.getSize();
                JComponent filler = (JComponent) Box.createRigidArea(dimension);

                // border
                Border dashedBorder = BorderFactory.createDashedBorder(Color.gray, 3f, 3f, 2f, true);
                filler.setBorder(dashedBorder);
                filler.setOpaque(true);

                return filler;
            }

            @Override
            public void mouseReleased(MouseEvent e) {
                Point transformedEventPoint = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), owner);
                Component compDroppedOn = SwingUtilities.getDeepestComponentAt(owner, transformedEventPoint.x, transformedEventPoint.y);
                if (compDroppedOn instanceof Filler) {
                    window.remove(floater);
                    swapComponents(floater, compDroppedOn);
                    window.setVisible(false);
                    floater = null;
                }
            }
        }

        private JWindow window;
        private MyMouseAdapter mouseAdapter;
        private DraggableDemo owner;

        DraggablePanel(DraggableDemo owner) {
            this.owner = owner;
            JPanel smallPanel = new JPanel();
            smallPanel.setPreferredSize(new Dimension(200, 100));
            smallPanel.setBackground(Color.green);

            JPanel bigPanel = new JPanel();
            bigPanel.setPreferredSize(new Dimension(200, 400));
            bigPanel.setBackground(Color.red);

            setLayout(new BorderLayout());
            add(smallPanel, BorderLayout.NORTH);
            add(bigPanel, BorderLayout.CENTER);

            setBorder(new LineBorder(Color.blue));

            mouseAdapter = new MyMouseAdapter();
            smallPanel.addMouseListener(mouseAdapter);
            smallPanel.addMouseMotionListener(mouseAdapter);
            owner.addMouseListener(mouseAdapter);
            owner.addMouseMotionListener(mouseAdapter);

            window = new JWindow();
            window.setAlwaysOnTop(true);
        }
    }

    private GridBagConstraints gbc;

    /**
     * 
     */
    public DraggableDemo() {
        setLayout(new GridBagLayout());
        gbc = new GridBagConstraints();
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.weightx = 1;

        for (int i = 0; i < 2; i++) {
            add(new DraggablePanel(this), gbc);
        }
    }

    private static void createAndShowGUI() {
        JFrame frame = new JFrame("Demo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        DraggableDemo newContentPane = new DraggableDemo();
        newContentPane.setOpaque(true);
        frame.setContentPane(newContentPane);

        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                createAndShowGUI();
            }
        });
    }

    /**
     */
    private void swapComponents(Component toAdd, Component toRemove) {
        Container parent = toRemove.getParent();
        int index = parent.getComponentZOrder(toRemove);

        parent.remove(toRemove);
        parent.add(toAdd, gbc, index);

        revalidate();
        repaint();

    }

}

我尝试过的:

原始容器每列包含几个组件,每个组件触发不同的鼠标事件。

最初我将鼠标监听器注册到主容器并根据坐标获取和移动组件,但这不满足鼠标进入/退出较小组件本身触发事件的条件。

在此之后,我尝试注册多个鼠标侦听器,每个侦听器都做自己的事情,但我了解到这些会占用层次结构中发生的事件。

最后,我决定为每个需要注册的组件注册一个监听器,并根据从事件返回的组件实例,将这些事件委托给适当的鼠标“适配器”。

如何修复组件以便在拾取时正确拖动?就像我说的那样,我不能只注册到主容器,因为那样我就无法访问较小组件触发的事件,并且我不能注册多个侦听器,因为事件会被第一个吃掉一个开火的。

【问题讨论】:

    标签: java swing mouselistener jwindow


    【解决方案1】:

    每当我拿起一个组件并将其自动添加到窗口时,该组件将停止触发拖动事件

    停止触发拖动事件的不仅仅是组件。似乎没有为任何组件生成拖动事件:

    在您的createAndShowGUI() 方法中,在框架可见后,我添加了:

    long eventMask = AWTEvent.MOUSE_MOTION_EVENT_MASK + AWTEvent.MOUSE_EVENT_MASK;
    
    Toolkit.getDefaultToolkit().addAWTEventListener( new AWTEventListener()
    {
        public void eventDispatched(AWTEvent e)
        {
            System.out.println(e.getID());
        }
    }, eventMask);
    

    应该显示为任何组件生成的所有鼠标事件。但是,一旦显示 Window,就不会为任何组件生成进一步的事件,从而确认您的问题。

    接下来我删除了上面的代码并用一个自定义的 EventQueue 替换它来简单地显示每个生成的事件:

    EventQueue queue = new EventQueue()
    {
        protected void dispatchEvent(AWTEvent event)
        {
            System.out.println(event);
            super.dispatchEvent(event);
        }
    };
    
    Toolkit.getDefaultToolkit().getSystemEventQueue().push(queue);
    

    现在我确实看到了所有鼠标拖动事件。

    所以也许您可以创建一个自定义 EventQueue 来处理在 DraggablePanel 上生成的鼠标事件?

    【讨论】:

    • 我最终放弃了这个想法,并在使用容器和较小的组件之间做出了妥协,使用可以向下或向上传播事件的中间容器。我认为问题在于,每当该组件被拾取并添加到窗口时,侦听器所附加到的组件将不再存在,因为它已从其容器中删除,因此基本上没有什么可以实际触发鼠标事件。谢谢你的帮助,不管怎样,你给了我一些我以前没用过的有用的调试工具。
    猜你喜欢
    • 2016-07-29
    • 1970-01-01
    • 2019-06-18
    • 1970-01-01
    • 1970-01-01
    • 2016-04-27
    • 1970-01-01
    • 2021-08-22
    • 1970-01-01
    相关资源
    最近更新 更多