3

カスタム コンポーネントでスイング ポップアップを表示する必要があります。ポップアップは、自分で非表示にするまで表示されたままにする必要がありますが、フォーカスを得るべきではありません。

次の方法でそれを行う他の開発者によって書かれたコードがあります。

       popupMenu = new JPopupMenu();
       popupMenu.add(myCustomComponent, BorderLayout.CENTER);
       popupMenu.setFocusable(false);
       popupMenu.setVisible(true);
       popupMenu.show(parentComponent, x, y);

これは機能しているように見えますが、バグがあります。ポップアップが表示されている場合、コンポーネントの外側で最初にマウスをクリックすると、ポップアップによって消費されます。したがって、フォーカスを別のコンポーネントに設定するには、2 回クリックする必要があります。

どうすれば修正できますか?または、ポップアップを作成する正しい方法は何ですか?

アップデート

最後に、短いコードフラグメントで問題を再現することができました。出発点を与えてくれた Guillaume Polet に感謝します。

コードは次のとおりです。

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.*;

public class TestJPopup {

    protected void initUI() {
        JFrame frame = new JFrame(TestJPopup.class.getSimpleName());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        final JTextField textField = new JTextField("Some text field");
        frame.add(textField, BorderLayout.WEST);
        final JButton buttonToHit = new JButton("Hit me");
        buttonToHit.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                JOptionPane.showMessageDialog(buttonToHit, "You hit the button successfully");
            }
        });
        frame.add(buttonToHit);
        frame.setSize(200, 100);
        frame.setVisible(true);

        final JPopupMenu popup = new JPopupMenu();
        popup.add(new JLabel("<html>Hey!<br>I'm the popup window!</html>"),
                BorderLayout.NORTH);
        popup.setFocusable(false);
        popup.setVisible(true);
        popup.show(textField, 60, 60);

        // I want to activate popup when user clicks in the text field
        textField.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if (popup != null) {
                    popup.show(textField, 60, 60);
                }
            }
        });
    }

    public static void main(String[] args) throws Exception {
        Class lnfClass = Class.forName("com.sun.java.swing.plaf.windows.WindowsLookAndFeel", true,
                Thread.currentThread().getContextClassLoader());
        LookAndFeel feel = (LookAndFeel) lnfClass.newInstance();
        UIManager.setLookAndFeel(feel);

        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new TestJPopup().initUI();
            }
        });
    }
}

2 つの重要な瞬間:

  • Windows のルック アンド フィールを使用 (デフォルトでは再現不可能)
  • メイン フレームのテキスト フィールドに接続されたマウス リスナー
4

5 に答える 5

4

答えではありませんが、あなたが説明した動作を現在再現できないSSCCEの例にすぎません。たぶん、このコードから始めて、エラーを再現し、修正された機能しないコードで投稿を編集してみてください。

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

public class TestJPopup {

    protected void initUI() {
        JFrame frame = new JFrame(TestJPopup.class.getSimpleName());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JLabel leftLabel = new JLabel("Left");
        frame.add(leftLabel, BorderLayout.WEST);
        final JButton buttonToHit = new JButton("Hit me");
        buttonToHit.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                JOptionPane.showMessageDialog(buttonToHit, "You hit the button successfully");
            }
        });
        frame.add(buttonToHit);
        frame.setSize(500, 400);
        frame.setVisible(true);
        JPopupMenu popupMenu = new JPopupMenu();
        popupMenu.add(new JLabel("<html>A Custom<br>component<br>made to<br> simulate <br>your custom component</html>"),
                BorderLayout.NORTH);
        JTextField textfield = new JTextField(30);
        popupMenu.add(textfield);
        popupMenu.setFocusable(false);
        popupMenu.setVisible(true);
        popupMenu.show(leftLabel, 20, 20);
        // Let's force the focus to be in a component in the popupMenu
        textfield.requestFocusInWindow();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new TestJPopup().initUI();
            }
        });
    }
}
于 2012-09-26T18:12:06.163 に答える
2

コメントでmKorbelによって提案された ,JWindowの代わりにを使用した可能な回避策を次に示します。JPopupMenu

import java.awt.*;
import java.awt.event.*;

import javax.swing.*;

public class TestJPopup {

    protected void initUI() {
        final JFrame frame = new JFrame(TestJPopup.class.getSimpleName());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        final JTextField textField = new JTextField("Some text field");
        frame.add(textField, BorderLayout.WEST);
        final JButton buttonToHit = new JButton("Hit me");
        buttonToHit.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                JOptionPane.showMessageDialog(buttonToHit, "You hit the button successfully");
            }
        });
        frame.add(buttonToHit);
        frame.setSize(200, 70);
        frame.setVisible(true);

        final JWindow popup = new JWindow();
        popup.getContentPane().add(new JLabel("<html>Hey!<br>I'm the popup window!</html>"),
                BorderLayout.NORTH);
        popup.setLocation(frame.getLocation().x + 60, frame.getLocation().y + 60);
        popup.pack();
        popup.setFocusable(false);
        popup.setVisible(true);

        // I want to activate popup when user clicks in the text field
        textField.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseReleased(MouseEvent e) {
                if (popup != null) {
                    popup.setVisible(true);
                    popup.setLocation(frame.getLocation().x + 60, frame.getLocation().y + 60);
                    popup.toFront();
                }
            }
        });

        textField.addFocusListener(new FocusAdapter() {
            @Override
            public void focusLost(FocusEvent e) {
                if (popup != null) {
                    popup.setVisible(false);
                }
            }
        });
    }

    public static void main(String[] args) throws Exception {
        Class lnfClass = Class.forName("com.sun.java.swing.plaf.windows.WindowsLookAndFeel", true,
                Thread.currentThread().getContextClassLoader());
        LookAndFeel feel = (LookAndFeel) lnfClass.newInstance();
        UIManager.setLookAndFeel(feel);

        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new TestJPopup().initUI();
            }
        });
    }
}
于 2012-09-28T11:27:28.263 に答える
2

迅速な調査および/または将来の読者のために、

  • この問題は再現可能であり、

    a) JPopup

    b) JMenu

  • jdk1.6.0_25およびjdk1.7.0_04でテスト済み

  • WinXpとで同じ問題が発生しましたWin7

  • へ/ 、Look and Feel_SystemLookAndFeelWindowsLookAndFeel

于 2012-09-27T17:29:29.473 に答える
2

解決策ではありませんが:

私にはバグのように見えますが、単純な componentPopup でも同じ誤動作を示します (Metal ではなく、winLAF と Nimbus で):

JTextField field = new JTextField("some popup owner");
JPopupMenu menu = new JPopupMenu();
menu.add("dummy");
field.setComponentPopupMenu(menu);
Action action = new AbstractAction("hit me!") {

    @Override
    public void actionPerformed(ActionEvent e) {
        LOG.info("got hit!");
    }
};
JComponent content = new JPanel();
content.add(new JButton(action));
content.add(field);
于 2012-09-27T11:38:34.787 に答える
0

問題を修正する魔法の行は次のとおりです。

UIManager.put("PopupMenu.consumeEventOnClose", Boolean.FALSE);

BasicPopupMenuUI クラスのソース コードを調べたところ、これが見つかりました。コード内の次のコメントによると、この動作は意図的な設計上の選択であるようですが、私にはバグのように感じます。

        // Ask UIManager about should we consume event that closes
        // popup. This made to match native apps behaviour.

ちなみに、Java 5 と 6 でも発生します。

于 2014-02-28T21:28:23.947 に答える