【问题标题】:JMenuItems on a JPopupMenu sometimes aren't drawnJPopupMenu 上的 JMenuItems 有时不会被绘制
【发布时间】:2015-10-23 17:01:11
【问题描述】:

我有一个 JPopupMenu,当按下 JButton 时会显示该菜单。此菜单包含一系列 JMenuItem,每个都与一个动作相关联,它们有时会发生变化。我遇到的问题是间歇性地没有绘制 JMenuItems,我只是得到一个灰色的 JPopupMenu,但是如果我将鼠标光标移到它们上面,这些项目就会出现。我认为问题可能在于更改后未正确重新绘制组件,但测试表明即使项目没有更改,问题也会继续发生。以下是相关代码:

    if (!listChanged) {
        myPopupMenu.show(myButton, x, y);
    } else {
        List<String> menuList = getMenuList();
        MyData data = getData();
        myPopupMenu.removeAll();

        for (int i = 0; i < menuList.size(); i++) {
            String name = menuList.get(i);
            JMenuItem item = new JMenuItem(new MyMenuAction(this, name,
                    data, i));
            item.addActionListener(this);
            myPopupMenu.add(item);
            myPopupMenu.validate();
        }
        myPopupMenu.repaint();
        myPopupMenu.show(myButton, x, y);
    }
...

private static class MyMenuAction extends AbstractAction {
    private MyClass parent;
    private int index;
    private MyData data;

    public MyMenuAction (MyClass parent, String name,
            MyData data, int index) {
        super(name);
        this.parent = parent;
        this.index = index;
        this.data = data;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        Object[] actionParameters;
        try {
            actionParameters = data.getParameters(index);
        } catch (ImmediateException e1) {
            log(e1.getMessage(), "Error");
            return;
        }
        parent.myButtonAction(actionParameters);
    }

}

只是为了澄清一下,这些操作运行良好,JPopupMenu 和所有 JMenuItems 的 10 次中有 8 次被正确绘制,但我无法弄清楚为什么它们有时不出现(无论列表是否已更改或不)。任何帮助将不胜感激。

编辑: 好的,按照 Andrew Thompson 的建议,这里有一个简短的完整示例。许多方法已经被剥离,但基本的仍然存在。只需单击并按住“SHOW MENU”按钮即可显示 JPopupMenu。由于问题是间歇性的,因此可能需要多次执行,直到问题出现。

package main;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JToolBar;
import javax.swing.UIManager;

public class MyClass implements ActionListener, MouseListener {
    boolean listChanged = true;
    boolean mousePressed = false;
    long clickStart;
    JPopupMenu myPopupMenu;
    JButton myButton;
    JFrame myFrame;
    ArrayList<String> list;

    public static void main(String[] args) {
        MyClass myClass = new MyClass();
        myClass.start();
    }

    private void start() {
        myFrame = new JFrame();
        myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception e) {
            System.out.println(e.getClass().getName() + " " + e.getMessage());
        }

        startList();
        myButton = new JButton("SHOW MENU");
        myPopupMenu = new JPopupMenu();

        JToolBar toolbar = new JToolBar();
        toolbar.add(new JButton("Button1"));
        toolbar.add(new JButton("Button2"));
        toolbar.add(new JButton("Button3"));
        toolbar.add(new JButton("Button4"));
        toolbar.add(new JButton("Button5"));
        toolbar.add(myButton);

        myButton.addMouseListener(this);

        toolbar.setBorder(BorderFactory.createEtchedBorder(1));
        JPanel emptyPanel = new JPanel();

        myFrame.add(toolbar, BorderLayout.PAGE_START);
        myFrame.add(emptyPanel, BorderLayout.CENTER);
        myFrame.pack();
        myFrame.setExtendedState(myFrame.getExtendedState()
                | JFrame.MAXIMIZED_BOTH);
        myFrame.setVisible(true);

    }

    public void showMenu() {
        if (!listChanged) {
            myPopupMenu.show(myButton, 0, myButton.getHeight());
        } else {
            listChanged = false;
            List<String> menuList = getMenuList();
            MyData data = getData();
            myPopupMenu.removeAll();

            for (int i = 0; i < menuList.size(); i++) {
                String name = menuList.get(i);
                JMenuItem item = new JMenuItem(new MyMenuAction(this, name,
                        data, i));
                item.addActionListener(this);
                myPopupMenu.add(item);
                myPopupMenu.validate();
            }
            myPopupMenu.repaint();
            myPopupMenu.show(myButton, 0, myButton.getHeight());
        }
    }

    private void startList() {
        list = new ArrayList<String>();
        list.add("Item 1");
        list.add("Item 2");
        list.add("Item 3");
        list.add("Item 4");
        list.add("Item 5");
    }

    private List<String> getMenuList() {
        return list;
    }

    public void myButtonAction() {
        Object[] defaultParameters = getDefaultParameters();
        myButtonAction(defaultParameters);
    }

    private Object[] getDefaultParameters() {
        // Placeholder
        return null;
    }

    public void myButtonAction(Object[] actionParameters) {
        // Placeholder
    }

    private MyData getData() {
        // Placeholder
        return new MyData();
    }

    private void changeList(List<String> newList) {
        list.clear();
        list.addAll(newList);
        listChanged = true;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        // Placeholder
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        // TODO Auto-generated method stub

    }

    @Override
    public void mousePressed(MouseEvent e) {
        if (e.getSource() == myButton) {
            mousePressed = true;
            clickStart = System.currentTimeMillis();
            new Thread(new Runnable() {

                @Override
                public void run() {
                    synchronized (this) {
                        while (mousePressed)
                            try {
                                this.wait(10);
                                if (System.currentTimeMillis() - clickStart > 300) {
                                    MyClass.this.showMenu();
                                    return;
                                }
                            } catch (InterruptedException e1) {
                                break;
                            }
                        MyClass.this.myButtonAction();
                    }
                }
            }).start();

        }
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        mousePressed = false;
    }

    @Override
    public void mouseEntered(MouseEvent e) {
        // TODO Auto-generated method stub

    }

    @Override
    public void mouseExited(MouseEvent e) {
        // TODO Auto-generated method stub

    }

    private static class MyData {

        public Object[] getParameters(int index) {
            // Placeholder
            return null;
        }

    }

    private static class MyMenuAction extends AbstractAction {
        private MyClass parent;
        private int index;
        private MyData data;

        public MyMenuAction(MyClass parent, String name, MyData data, int index) {
            super(name);
            this.parent = parent;
            this.index = index;
            this.data = data;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            Object[] actionParameters;
            try {
                actionParameters = data.getParameters(index);
            } catch (Exception e1) {
                System.out.println(e1.getMessage());
                return;
            }
            parent.myButtonAction(actionParameters);
        }
    }

}

【问题讨论】:

  • 有时? 请参阅Initial Threads 并考虑setComponentPopupMenu()
  • "这里是相关代码:"如需更好的帮助,请尽快发帖minimal reproducible exampleShort, Self Contained, Correct Example
  • @trashgod 我的鼠标监听器上确实有一个线程,因为如果我按住它一段时间,它应该只显示 JPopupMenu(如果我只是单击,它会执行更简单的操作),但我不明白这怎么会导致我的问题,因为菜单已显示,但只有 JMenuItems 没有被绘制。
  • I do have a thread on my mouse listener, - 那么也许鼠标监听器就是问题所在。您是否意识到只有在同一位置生成 mousePressed 和 mouseReleased 事件时才会生成 mouseClick。因此,如果当您按住鼠标一段时间并且鼠标移动一个像素时,您将不会收到 mouseClicked 事件。这可能就是为什么它是随机的。所以基本上你发布的代码(可能)与问题无关。这就是为什么 Andres 要求的可运行示例应该随每个问题发布。
  • @camickr 我已经这样做了,我在回复垃圾神之前编辑了我的问题

标签: java swing jmenuitem jpopupmenu


【解决方案1】:

我有一个 JPopupMenu,当按下 JButton 时会显示它。

首先,我对这样的 UI 有疑问。标准是在您右键单击时显示一个弹出窗口(在 Windows 中)。遵循已知标准。阅读 Bringing Up a Popup Menu 上的 Swing 教程部分,了解更多信息和工作示例。

其次,我无法重现该问题(无论我尝试多长时间)。随机问题通常是由未在 EDT 上更新 GUI 引起的。所以不要使用线程。

改为使用Swing Timer

Timer 设置为在显示菜单的 200 毫秒后触发。在 EDT 上调用从 Timer 执行的代码。所以你会在mousePressedrestart()Timer。和stop() TimermouseReleased

【讨论】:

  • 我不能真正使用不同的行为(右键单击弹出窗口),因为这是为了工作,我必须遵循所需的内容。有人告诉我,原因是他们希望它的功能类似于网络浏览器上的后退按钮,单击一次会将您带到最后一页并按住会显示最后一页的历史记录。我会尝试更改为使用计时器,看看它是否有效
  • I was told the reason was that they wanted it to function like the back button on web browsers, where clicking once takes you to the last page and holding shows a history of the last pages. - 很有趣,我从来不知道这个功能存在。我总是只使用右键单击来获取历史记录。我猜你每天都会学到一些新东西:)
  • 添加定时器似乎已经解决了这个问题,所以感谢@camickr。不过,我不明白这个问题的根本原因。如果有人能解释为什么代码可以在绘制 JPopupMenu 而不是它的 JMenuItems 方面起作用,我将不胜感激。
  • @FredLage,这是一个线程问题。我不知道为什么事情有时会起作用,有时却不起作用的细节。关键是所有 GUI 组件都应该在 EDT 上创建和更新(或者您可能会遇到随机问题)。这是一个应该始终遵循的规则,这样您就不会花时间追逐随机问题:) 阅读 Concurrency 上的 Swing 教程中的部分以获取更多信息。
猜你喜欢
  • 1970-01-01
  • 2011-08-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-17
  • 1970-01-01
相关资源
最近更新 更多