我正在开发一个个人项目,按钮的部分功能是右键单击它们时会显示一个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;
}
}
这是MenuItem
的ActionListener
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) {
}
}
}
}
}
这是 PopUpMenu
的 ActionListener
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;
}
}
这是相同逻辑的重构版本,不需要大量专门的(扩展)类,例如 PopUPMenu
或 MenuItem
。
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/