Как выбрать первый элемент в JPopupMenu? - PullRequest
6 голосов
/ 27 января 2009

В прошлом, когда кто-то делал JPopupMenu видимым, его первый элемент выбирался по умолчанию: http://weblogs.java.net/blog/alexfromsun/archive/2008/02/jtrayicon_updat.html

В настоящее время стандартным поведением является всплывающее меню без какого-либо выбранного элемента. Я хотел бы создать JPopupMenu с одним элементом, который будет всплывать выделенным и по центру под указателем мыши. Мне удалось заставить элемент всплывать по центру под мышкой, но я JMenuItem отказывается отображать, как будто он выбран. Если я вытащу мышь из предмета и вернусь в нее, то выберусь правильно.

Есть идеи?

Вот мой тестовый пример:

import java.awt.Component;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;

public class Test extends JFrame
{
    public static void main(String[] args)
    {
        JFrame frame = new JFrame();
        frame.setSize(800, 600);
        frame.getContentPane().addMouseListener(new MouseAdapter()
        {
            @Override
            public void mousePressed(MouseEvent e)
            {
                if (e.isPopupTrigger())
                    popupTriggered(e);
            }

            @Override
            public void mouseReleased(MouseEvent e)
            {
                if (e.isPopupTrigger())
                    popupTriggered(e);
            }

            private void popupTriggered(MouseEvent e)
            {
                JPopupMenu menu = new JPopupMenu();
                final JMenuItem item = new JMenuItem("This is a JMenuItem");
                menu.add(item);
                Point point = e.getPoint();
                int x = point.x - (item.getPreferredSize().width / 2);
                int y = point.y - (item.getPreferredSize().height / 2);
                menu.show((Component) e.getSource(), x, y);
            }
        });
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.setVisible(true);
    }
}

Ответы [ 4 ]

6 голосов
/ 28 ноября 2009

Секрет оказывается MenuSelectionManager.defaultManager().setSelectedPath(new MenuElement[]{menu, ...});

import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.MenuElement;
import javax.swing.MenuSelectionManager;
import javax.swing.SwingUtilities;

/**
 * Demonstrates programmatic {@link JMenuItem} selection;
 * specifically how to make the first item selected by default
 */
public class TestPopup extends JFrame {
  public static void main(String[] args) {
    final JFrame frame = new JFrame("TestPopup");
    frame.setSize(640, 480);
    frame.getContentPane().addMouseListener(new MouseAdapter() {
      @Override
      public void mousePressed(MouseEvent e) {
        if (e.isPopupTrigger()) {
          popupTriggered(e);
        }
      }
      private void popupTriggered(MouseEvent e) {
        final JPopupMenu menu = new JPopupMenu();
        final JMenuItem item0 = new JMenuItem("JMenuItem 0");
        final JMenuItem item1 = new JMenuItem("JMenuItem 1");
        menu.add(item0);
        menu.add(item1);
        menu.pack();
        // use invokeLater or just do this after the menu has been shown
        SwingUtilities.invokeLater(new Runnable() {
          public void run() {
            MenuSelectionManager.defaultManager().setSelectedPath(new MenuElement[]{menu, item0});
          }
        });
        int x = (int) ((int) (frame.getSize().width - (menu.getPreferredSize().width / 2.)) / 2.);
        int y = (int) ((int) (frame.getSize().height - (menu.getPreferredSize().height / 2.)) / 2.);
        menu.show(frame, x, y);
        // doesn't work:
        //item0.setSelected(true);
        // doesn't work:
        //menu.getSelectionModel().setSelectedIndex(0);
        // bingo; see also MenuKeyListener / MenuKeyEvent
//        MenuSelectionManager.defaultManager().setSelectedPath(new MenuElement[]{menu, item0});
      }
    });
    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
}
1 голос
/ 13 октября 2011

MenuSelectionManager.defaultManager () действительно является хорошим решением, но оно не будет работать, если вы попытаетесь предварительно выбрать подменю вашего JPopupMenu (оно скроет родительское меню). Кроме того, он портит другие способы навигации с помощью клавиатуры (вы не можете нажать левую кнопку, чтобы скрыть подменю и т. Д.)

К сожалению, в Swing нет хорошего решения этого вопроса ... Мое решение уродливо, но, к сожалению, отлично справляется с работой:

public static void setMenuSelectedIndex(final JPopupMenu popupMenu, final int index) {
    SwingUtilities.invokeLater(new Runnable(){public void run()
    {
        for (int i=0; i < index+1; i++) {
            popupMenu.dispatchEvent(new KeyEvent(popupMenu, KeyEvent.KEY_PRESSED, 0, 0, KeyEvent.VK_DOWN, '\0'));
        }
    }});
}

Как вы видите, я в основном имитирую нажатия клавиш "Вниз" во всплывающем меню ...

Лучшим решением может быть не жестко имитировать VK_DOWN, а прочитать карту ввода Popup и определить, какой KeyCode означает «выбрать следующий пункт меню» - но я думаю, что большинство из нас справится с этим хаком ...

Возможно, вы также захотите взглянуть на этот метод, который выбирает пункт меню после его выбора. Используется предыдущий метод

public static void setSelectedIndexWhenVisible(final JMenu menu, final int index) {
    menu.getPopupMenu().addPopupMenuListener(new PopupMenuListener() {
        @Override
        public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
            PopupUtils.setMenuSelectedIndex(menu.getPopupMenu(), index);
            menu.getPopupMenu().removePopupMenuListener(this);
        }

        @Override
        public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
        }

        @Override
        public void popupMenuCanceled(PopupMenuEvent e) {
        }
    });
}
0 голосов
/ 28 января 2009

Это странно.

Я пробовал это с Windows, и с Java 1.5.0_08 и даже 1.6.0_07 первый элемент выбирается автоматически, как вы и ожидали быть.

Итак, я попробовал его с 1.6.0_11 , и он больше не работает, первый элемент изначально не выбран. Выбор элемента в selectionModel, похоже, не помогает.

Одним из обходных путей (которым я совсем не горжусь) является автоматическое перемещение мыши после отображения всплывающего меню с использованием координат MouseEvent . Может, у кого-то есть идея получше?

import java.awt.AWTException;
import java.awt.Robot;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;

public class SelectedPopupMenu extends JFrame {

    public SelectedPopupMenu() {
        addMouseListener(new MouseAdapter() {
            public void mouseClicked(final MouseEvent e) {
                JPopupMenu popupMenu = new JPopupMenu();
                popupMenu.add(new JMenuItem("Test-Item"));
                popupMenu.add(new JMenuItem("Test-Item-2"));
                // do not care to really hit the center of the popup
                popupMenu.show(SelectedPopupMenu.this, e.getX() - 30, e.getY() - 10);
                try {
                    // shake mouse, so that first element is selected even in Java 1.6.0_11
                    Robot robot = new Robot();
                    robot.mouseMove(e.getX() + 1, e.getY());
                    robot.mouseMove(e.getX(), e.getY());
                } catch (AWTException ex) {
                    ex.printStackTrace();
                }
            }
        });
    }

    public static void main(String[] args) {
        JFrame frame = new SelectedPopupMenu();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(800, 600);
        frame.setVisible(true);
    }
}
0 голосов
/ 27 января 2009

В настоящее время стандартным поведением является всплывающее меню без выбранного элемента.

На самом деле, я бы сказал, что это правильное поведение, по крайней мере, в Windows. Другие не Java-приложения тоже делают это. Я не думаю, что стоит нарушать это соглашение, даже если в меню есть только один пункт. Если вы чувствуете иначе, вы можете установить индекс выбора как в sean.bright's answer .


Итак, я наконец-то получил возможность опробовать его на Java 1.6.0_11 и обнаружил некоторое противоречивое поведение: если всплывающее меню выходит из родительского фрейма, элемент выбирается автоматически; если всплывающее меню полностью отображается в родительском фрейме, ничего не выбрано. Походит на ошибку Swing, которая по крайней мере гарантирует RFE для последовательного поведения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...