java - JMenuItem 的 ActionListener 不起作用

标签 java swing actionlistener menuitem popupmenu

我正在开发一个个人项目,按钮的部分功能是右键单击它们时会显示一个PopMenu。该代码直到昨天才起作用,但今天我尝试使其更加OO,现在,尽管菜单确实显示,但当我单击MenuItems时,没有任何反应。不幸的是,我没有版本控制,所以我没有旧版本的副本。

代码如下:

这是PopUpMenu

public class PopUpMenu extends JPopupMenu {
    private Container parent;

    public PopUpMenu(MenuItem[] menuItems) {
        super();
        for (MenuItem item : menuItems) {
            add(item);
        }
    }

    public Container getParent() {
        return parent;
    }

    public void setParent(Container parent) {
        this.parent = parent;
        parent.addMouseListener(new PopUpListener(this));
    }

}

这是实际的MenuItem

public class MenuItem extends JMenuItem {
    private String methodName;

    public MenuItem(String methodName, String text) {
        super(text);
        setMethodName(methodName);
        setFocusable(true);
        addActionListener(new MenuItemListener());
    }

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

}

这是MenuItemActionListener

public class MenuItemListener extends IListener {

    protected void action(ActionEvent event) {
        Object source = event.getSource();
        if (source instanceof MenuItem) {
            MenuItem item = (MenuItem) source;
            Container parent = item.getParent();
            if (parent instanceof PopUpMenu) {
                PopUpMenu menu = (PopUpMenu) parent;
                Container container = menu.getParent();
                try {
                    String name = item.getMethodName();
                    Method method = container.getClass().getMethod(name);
                    method.invoke(container);
                } catch (Exception e) {
                }
            }
        }
    }

}

这是 PopUpMenuActionListener

public class PopUpListener extends MouseAdapter {
    private PopUpMenu menu;

    public PopUpListener(PopUpMenu menu) {
        setMenu(menu);
    }

    public void mouseReleased(MouseEvent event) {
        if (event.isPopupTrigger()) {
            menu.show(event.getComponent(), event.getX(), event.getY());
        }
    }

    public PopUpMenu getMenu() {
        return menu;
    }

    public void setMenu(PopUpMenu menu) {
        this.menu = menu;
    }

}

这是抽象类 IListener

public abstract class IListener implements ActionListener {
    private boolean keyboardSensitive;

    public IListener() {
        setKeyboardSensitive(false);
    }

    @Override
    public void actionPerformed(ActionEvent event) {
        if ((event.getModifiers() != 0) || isKeyboardSensitive()) {
            action(event);
        }
    }

    protected abstract void action(ActionEvent event);

    public boolean isKeyboardSensitive() {
        return keyboardSensitive;
    }

    public void setKeyboardSensitive(boolean keyboardSensitive) {
        this.keyboardSensitive = keyboardSensitive;
    }

}

经过一些测试,我发现 ActionListener 实际上是通过按键激活的,而不是通过鼠标的任何按钮激活的,(通过在调试期间删除 if 发现)这不是很有帮助因为正如您在 IListener 类中看到的那样,我不想接受来自键盘的任何事件。

IListener 也是我在程序中使用的所有其他 ActionListener 的基础,它似乎对它们工作得很好。

所以本质上我的问题是:我需要修复什么才能通过鼠标点击激活 MenuItemListener

最佳答案

PopUpMenu 中删除 getParent 方法

public class PopUpMenu extends JPopupMenu {
    private Container parent;

    public PopUpMenu(MenuItem[] menuItems) {
        super();
        for (MenuItem item : menuItems) {
            add(item);
        }
    }

    public void setParent(Container parent) {
        this.parent = parent;
        parent.addMouseListener(new PopUpListener(this));
    }

}

此方法将覆盖 java.awt.Component.getParent() 中定义的 getParent。我想这会导致意外的行为。

编辑

I'm overriding that method on purpose. But I still tried to remove it to see if that would fix the problem. Unfortunately, it did not.

您可以重写该方法,但必须确保 Component.getParent method's contract .

PopUpMenu 不是容器父级 的子级。我的意思是,如果 PopUpMenu 返回容器 parent,容器也应该知道 PopUpMenu 是它的子容器。例如。 Container.getCompnents()应包含 PopUpMenu。这就是契约(Contract)。

但这对您的情况没有帮助,因为实际上并不想创建组件父/子关系。您只想保存对要调用某些 method.invoke(container); 的某个对象的引用。

此示例基于您的代码以及我上面建议的修复。我已将所有内容放入一个编译单元中以提供 MVCE :

import java.awt.*;
import java.awt.event.*;
import java.lang.reflect.*;
import javax.swing.*;

public class Main {

    public static void main(String[] args) {
        JFrame frame = createFrame();

        MenuItem menuItem1 = new MenuItem("getForeground", "Foreground Color");
        MenuItem menuItem2 = new MenuItem("getBackground", "Background Color");
        PopUpMenu popUpMenu = new PopUpMenu(new MenuItem[] { menuItem1, menuItem2 });
        popUpMenu.setParent(frame);

        frame.setVisible(true);
    }

    private static JFrame createFrame() {
        JFrame frame = new JFrame();
        frame.setSize(1000, 800);
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        return frame;
    }

}


class PopUpMenu extends JPopupMenu {

    private Container parent;

    public PopUpMenu(MenuItem[] menuItems) {
        super();
        for (MenuItem item : menuItems) {
            add(item);
        }
    }

    public Container getParentComponent() {
        // another name because I don't want to override getParent()
        // Try to rename this method to getParent to see 
        // that it will not work
        return parent;
    }

    public void setParent(Container parent) {
        this.parent = parent;
        parent.addMouseListener(new PopUpListener(this));
    }

}

class MenuItemListener extends IListener {

    protected void action(ActionEvent event) {
        Object source = event.getSource();
        if (source instanceof MenuItem) {
            MenuItem item = (MenuItem) source;
            Container parent = item.getParent();
            if (parent instanceof PopUpMenu) {
                PopUpMenu menu = (PopUpMenu) parent;
                Container container = menu.getParentComponent();
                try {
                    String name = item.getMethodName();
                    Method method = container.getClass().getMethod(name);
                    Object invoke = method.invoke(container);
                    JOptionPane.showMessageDialog(container, invoke);
                } catch (Exception e) {
                }
            }
        }
    }

}


abstract class IListener implements ActionListener {
    private boolean keyboardSensitive;

    public IListener() {
        setKeyboardSensitive(false);
    }

    @Override
    public void actionPerformed(ActionEvent event) {
        if ((event.getModifiers() != 0) || isKeyboardSensitive()) {
            action(event);
        }
    }

    protected abstract void action(ActionEvent event);

    public boolean isKeyboardSensitive() {
        return keyboardSensitive;
    }

    public void setKeyboardSensitive(boolean keyboardSensitive) {
        this.keyboardSensitive = keyboardSensitive;
    }

}

class MenuItem extends JMenuItem {
    private String methodName;

    public MenuItem(String methodName, String text) {
        super(text);
        setMethodName(methodName);
        setFocusable(true);
        addActionListener(new MenuItemListener());
    }

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

}

class PopUpListener extends MouseAdapter {
    private PopUpMenu menu;

    public PopUpListener(PopUpMenu menu) {
        setMenu(menu);
    }

    @Override
    public void mousePressed(MouseEvent event) {
        if (event.isPopupTrigger()) {
            menu.show(event.getComponent(), event.getX(), event.getY());
        }

    }

    public void mouseReleased(MouseEvent event) {
        if (event.isPopupTrigger()) {
            menu.show(event.getComponent(), event.getX(), event.getY());
        }
    }

    public PopUpMenu getMenu() {
        return menu;
    }

    public void setMenu(PopUpMenu menu) {
        this.menu = menu;
    }

}

这是相同逻辑的重构版本,不需要大量专门的(扩展)类,例如 PopUPMenuMenuItem

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.lang.reflect.*;
import java.text.MessageFormat;

import javax.swing.*;

public class Main {

    public static void main(String[] args) {
        JFrame frame = createFrame();

        JMenuItem foregroundMenuItem = createMenuItem(frame, "getForeground", "Foreground Color");
        JMenuItem backgroundMenuItem = createMenuItem(frame, "getBackground", "Background Color");

        JPopupMenu popupMenu = new JPopupMenu();

        popupMenu.add(foregroundMenuItem);
        popupMenu.add(backgroundMenuItem);

        PopUpListener popUpListener = new PopUpListener(popupMenu);
        frame.addMouseListener(popUpListener);

        frame.setVisible(true);
    }

    private static JMenuItem createMenuItem(Object invocationTarget, String methodName, String actionName) {
        MethodInvocationAction methodInvocationAction = new MethodInvocationAction(invocationTarget, methodName);
        methodInvocationAction.putValue(Action.NAME, actionName);

        JMenuItem menuItem = new JMenuItem(methodInvocationAction);
        return menuItem;
    }

    private static JFrame createFrame() {
        JFrame frame = new JFrame();
        frame.setSize(1000, 800);
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        return frame;
    }

}

class MethodInvocationAction extends AbstractAction {

    private Object targetObj;
    private Method targetMethod;

    private boolean keyboardSensitive;

    public MethodInvocationAction(Object targetObj, String methodName) {
        this.targetObj = targetObj;
        try {
            targetMethod = targetObj.getClass().getMethod(methodName);
        } catch (NoSuchMethodException | SecurityException e) {
            String msg = MessageFormat.format("{0} does not have a method named {1}", targetObj, methodName);
            throw new RuntimeException(msg, e);
        }
    }

    public boolean isKeyboardSensitive() {
        return keyboardSensitive;
    }

    public void setKeyboardSensitive(boolean keyboardSensitive) {
        this.keyboardSensitive = keyboardSensitive;
    }

    @Override
    public void actionPerformed(ActionEvent event) {
        if ((event.getModifiers() != 0) || isKeyboardSensitive()) {
            performAction(event);
        }
    }

    public void performAction(ActionEvent e) {
        try {
            Object invoke = targetMethod.invoke(targetObj);
            JOptionPane.showMessageDialog(null, invoke);
        } catch (Exception exception) {
            showException(exception);
        }
    }

    private void showException(Exception e1) {
        StringWriter exceptionStackTraceWriter = new StringWriter();
        e1.printStackTrace(new PrintWriter(exceptionStackTraceWriter));
        String exceptionStackTrace = exceptionStackTraceWriter.toString();

        JTextArea exceptionStackTraceTextComponent = new JTextArea();
        exceptionStackTraceTextComponent.setText(exceptionStackTrace);

        JScrollPane scrollPane = new JScrollPane(exceptionStackTraceTextComponent);
        scrollPane.setPreferredSize(new Dimension(800, 600));

        JOptionPane.showMessageDialog(null, scrollPane, e1.getLocalizedMessage(), JOptionPane.ERROR_MESSAGE);
    }
}

class PopUpListener extends MouseAdapter {
    private JPopupMenu menu;

    public PopUpListener(JPopupMenu menu) {
        this.menu = menu;
    }

    public void mousePressed(MouseEvent event) {
        handlePopupEvent(event);
    }

    public void mouseReleased(MouseEvent event) {
        handlePopupEvent(event);
    }

    private void handlePopupEvent(MouseEvent event){
        if (event.isPopupTrigger()) {
            menu.show(event.getComponent(), event.getX(), event.getY());
        }
    }

}

关于java - JMenuItem 的 ActionListener 不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39913371/

相关文章:

java - 应该在 java @ 1.5 中将 String 转换为 Int 还是使用其他方法?

java - 为什么 javax.xml.stream.XMLEventReader.next() 在编译时无法转换为 XMLEvent?

java - 我的 Hibernate CRUD 没有在表中保存数据,而是显示表为空

java - ITestListener - ExtentReport

java - 动态启用和禁用 JTable 排序

java - 单击按钮后更改框架的内容 Pane

Java Swing 不显示 Unicode 字符

java - 具有多个输入选项的 ActionEvent e.getsource()

java - Java 中的 Action 监听器

java - jButton1ActionPerformed 和 jButton2ActionPerformed 如何共享 BufferedReader/BufferedWriter?