我正在开发一个个人项目,按钮的部分功能是在右键单击时显示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
?
答案 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;
}
}
以下是相同逻辑的重构版本,不需要很多专门(扩展)类,如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());
}
}
}