JMenuItem的ActionListener无法正常工作

时间:2016-10-07 08:59:22

标签: 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;
    }

}

这是ActionListener

MenuItem
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) {
                }
            }
        }
    }

}

这是ActionListener

PopUpMenu
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;
    }

}

这是abstract class 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实际上是通过按下按键激活的,而不是鼠标的任何按钮激活的(通过去除是否在调试过程中发现)这对你来说不是很有帮助可以在IListener类中看到我不想接受来自键盘的任何事件。

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

因此,我的问题是:我需要修复什么才能通过鼠标点击激活MenuItemListener

1 个答案:

答案 0 :(得分:2)

getParent

中删除PopUpMenu方法
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));
    }

}

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

修改

  

我故意重写这种方法。但我仍然试图删除它,看看是否能解决问题。不幸的是,它没有。

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

PopUpMenu不是容器parent的子代。我的意思是,如果PopUpMenu返回容器parent,容器也应该知道PopUpMenu是它的孩子。例如。 Container.getCompnents()应包含PopUpMenu。这是合同。

但是,由于并非真正想要创建一个组件父/子关系,因此它无助于您的情况。您只想保留对某些要调用某些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());
        }
    }

}