2016-10-07 7 views
2

Я работаю над личным проектом, и часть функциональности кнопок состоит в том, что они отображаются в PopMenu, когда их щелкают правой кнопкой мыши. Код работал до вчерашнего дня, но сегодня я попытался сделать его более OO, и теперь, хотя в меню ничего не появляется, я нажимаю MenuItems. У меня, к сожалению, нет контроля версий, поэтому у меня нет старой версии для вас.The ActionListener JMenuItem не работает

код приведен ниже:

Это 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

Можете выложить класс 'PopUpListener', пожалуйста? – hamena314

+3

Без этого я не могу проверить ваш код. Когда вы создаете [mcve], люди могут просто скопировать ваш код и посмотреть, что может быть не так с ним на их машине. – hamena314

ответ

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(). Я предполагаю, что это приводит к неожиданному поведению.

EDIT

Я перекрывая этот метод на цели. Но я все же пытался удалить его, чтобы проверить, не устранит ли это проблему. К сожалению, этого не произошло.

Вы можете переопределить метод, но вы должны обеспечить Component.getParentmethod'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()); 
     } 
    } 

} 
+0

Я использую этот метод специально. Но я все же пытался удалить его, чтобы проверить, не устранит ли это проблему. К сожалению, этого не произошло. –

+0

Я предполагаю, что вы просто добавили этот код: 'Object invoke = method.invoke (container); JOptionPane.showMessageDialog (container, invoke); 'показать, что программа работает, и я должен удалить ее после тестирования. Верный? –

+0

Также почему вы добавили метод 'mousClicked()' в класс 'PopUpListener'. –