swing mouse listeners being intercepted by child components

▼魔方 西西 提交于 2019-11-28 08:05:38

问题


I have a swing component that has several sub components. What I want to do change some label if the mouse is over any of those components, and then change it to something else if the mouse moves off all of the components. I'm trying to find a more efficient way to do this.

Currently I have mouse listeners over all of the child components that look something like:

class AMouseListener extends MouseAdapter {
    private boolean mouseOver;
    mouseEntered(MouseEvent e) { mouseOver = true; updateLabel(); }
    mouseExited(MouseEvent e) { mouseOver = false; updateLabel(); }

    void updateLabel() {
       String text = "not-over-any-components";
       // listeners are each of the listeners added to the child components
       for ( AMouseListener listener :listeners ) {
          if ( listener.mouseOver ) {
             text = "over-a-component";
             break;
          }
       }
    }
}

This works, but I feel like there should be a better way to handle this by only handling mouseEntered and mouseExited events on the parent container, but since the child components intercept these events, I'm not sure how to go about doing this (I don't necessarily have control over the child components so I Can't forward the mouse events to the parent event if I wanted to).


回答1:


For example

import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;

public class TestMouseListener {

    public static void main(String[] args) {
        final JComboBox combo = new JComboBox();
        combo.setEditable(true);
        for (int i = 0; i < 10; i++) {
            combo.addItem(i);
        }
        final JLabel tip = new JLabel();
        tip.setPreferredSize(new Dimension(300, 20));
        JPanel panel = new JPanel();
        panel.add(combo);
        panel.add(tip);
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.add(panel);
        frame.pack();
        frame.setVisible(true);
        panel.addMouseListener(new MouseAdapter() {

            @Override
            public void mouseEntered(MouseEvent e) {
                tip.setText("Outside combobox");
            }

            @Override
            public void mouseExited(MouseEvent e) {
                Component c = SwingUtilities.getDeepestComponentAt(
                   e.getComponent(), e.getX(), e.getY());
                // doesn't work if you move your mouse into the combobox popup
                tip.setText(c != null && SwingUtilities.isDescendingFrom(
                   c, combo) ? "Inside combo box" : "Outside combobox");
            }
        });
    }

    private TestMouseListener() {
    }
}



回答2:


Check out the docs and examples for the "glass pane".
This should give you what you need: The Glass Pane




回答3:


You could initiate a single instance of the listener and add that instance to each component. Like this:

AMouseListener aMouseListener=new  AMouseListener();

for each(Component c:components) {
caddMouseListener(aMouseListener);
}



回答4:


I know this is very old, but here's a simple solution with which you can create a mouse listener for a component and all components inside it's bounds (without adding the listener to all components individually):

/**
 * Creates an {@link AWTEventListener} that will call the given listener if
 * the {@link MouseEvent} occurred inside the given component, one of its
 * children or the children's children etc. (recursive).
 * 
 * @param component
 *            the component the {@link MouseEvent} has to occur inside
 * @param listener
 *            the listener to be called if that is the case
 */
public static void addRecursiveMouseListener(final Component component, final MouseListener listener) {
    Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {

        @Override
        public void eventDispatched(AWTEvent event) {
            if(event instanceof MouseEvent) {
                MouseEvent mouseEvent = (MouseEvent) event;
                if(mouseEvent.getComponent().isShowing() && component.isShowing()){
                    if (containsScreenLocation(component, mouseEvent.getLocationOnScreen())) {
                        if(event.getID() == MouseEvent.MOUSE_PRESSED) {
                            listener.mousePressed(mouseEvent);
                        }
                        if(event.getID() == MouseEvent.MOUSE_RELEASED) {
                            listener.mouseReleased(mouseEvent);
                        }
                        if(event.getID() == MouseEvent.MOUSE_ENTERED) {
                            listener.mouseEntered(mouseEvent);
                        }
                        if(event.getID() == MouseEvent.MOUSE_EXITED) {
                            listener.mouseExited(mouseEvent);
                        }
                        if(event.getID() == MouseEvent.MOUSE_CLICKED){
                            listener.mouseClicked(mouseEvent);
                        }
                    }
                }
            }
        }
    }, AWTEvent.MOUSE_EVENT_MASK);
}

/**
 * Checks if the given location (relative to the screen) is inside the given component
 * @param component the component to check with
 * @param screenLocation the location, relative to the screen
 * @return true if it is inside the component, false otherwise
 */
public static boolean containsScreenLocation(Component component, Point screenLocation){
    Point compLocation = component.getLocationOnScreen();
    Dimension compSize = component.getSize();
    int relativeX = screenLocation.x - compLocation.x;
    int relativeY = screenLocation.y - compLocation.y;
    return (relativeX >= 0 && relativeX < compSize.width && relativeY >= 0 && relativeY < compSize.height);
}

Note: Once the mouse exits the root component of this listener the mouseExited(mouseEvent) will probably not fire, however you can just add the mouse listener to the root component itself and it should fire.
mouseExited(mouseEvent) is unreliable in general though.



来源:https://stackoverflow.com/questions/11091531/swing-mouse-listeners-being-intercepted-by-child-components

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!