Java Swing:显示JPopupMenu后,插入符侦听器停止触发 [英] Java Swing: Caret-listener stops firing after JPopupMenu is shown

查看:80
本文介绍了Java Swing:显示JPopupMenu后,插入符侦听器停止触发的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在制作一个文本编辑器应用程序,但是在显示JPopupMenu后键入或粘贴文本时,我的CaretListener没有被触发。

I'm making a text-editor application, and I've run into an issue with my CaretListener not being fired when text is typed or pasted after a JPopupMenu is shown.

我已将CaretListener添加到具有

I have added the CaretListener to a JTextArea with

textArea.addCaretListener(new CaretListener() {
        public void caretUpdate(CaretEvent e) {
            runThisMethod();
        }
    });

此方法工作正常,插入符号移动时调用 runThisMethod()(按键,选择文本)等)。在我的应用程序中,我有一个JMenuBar和一个使用 textArea.setComponentPopupMenu(popupMenu); 添加的JPopupMenu。

This works fine, and calls "runThisMethod()" whenever the caret moves (key press, text selection, etc...). In my application I have a JMenuBar, and also a JPopupMenu added using textArea.setComponentPopupMenu(popupMenu);.

我的问题是,无论何时关闭弹出窗口(通过选择menuItem或通过单击JTextArea以外的任何位置来取消弹出窗口),CaretListener都会停止触发任何键输入(包括粘贴)。单击JTextArea中的任何位置将使其再次工作,并且将再次调用它进行键输入。使用JMenuBar不会不会触发此问题。

My issue is whenever the popup is closed (ether by selecting a menuItem, or cancelling it by clicking anywhere but the JTextArea), the CaretListener stops firing for any key input (including pasting). Clicking anywhere in the JTextArea will get it working again, and it will again be called for key input. Using the JMenuBar does not trigger this issue.

以下是演示该问题的代码示例(对不起,长度):

Here is a code sample that demonstrates the issue (Sorry for the length):

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.DefaultEditorKit;

public class GUI {

    private JFrame mainFrame;
    private JTextArea textArea;
    private JLabel posLabel;

    public GUI()
    {
        mainFrame = new JFrame("Untitled");
        mainFrame.setSize(800, 400);
        mainFrame.setLocationRelativeTo(null);
        mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        initPanel((JPanel)mainFrame.getContentPane());

        mainFrame.setVisible(true);
    }

    private void initPanel(JPanel panel)
    {
        textArea = new JTextArea();
        initMenu();

        panel.setLayout(new BorderLayout());
        JPanel textPanel = new JPanel();
        textPanel.setLayout(new GridLayout(1,1));
        //Set some more stuff...

        //KeyListener works, and seams to show that the JTextArea is focused.
        textArea.addKeyListener(new KeyListener() {

            @Override
            public void keyTyped(KeyEvent e){
                System.out.println("Key Update!");
                System.out.println(mainFrame.getFocusOwner().toString());
            }
            @Override
            public void keyPressed(KeyEvent e){}
            @Override
            public void keyReleased(KeyEvent e){}
            });

        //
        //CaretListener:
        //
        textArea.addCaretListener(new CaretListener() {

            @Override
            public void caretUpdate(CaretEvent e) {

                SwingUtilities.invokeLater(new Runnable(){      //Not sure if this is needed? Have tried with and without.

                    @Override
                    public void run() {

                        System.out.println("Caret Update!");
                        System.out.println(mainFrame.getFocusOwner().toString());
                        UpdatePosLabel();

                        //Do more stuff
                    }
                });
            }
        });

        textArea.addFocusListener(new FocusListener(){      //Updates Position once when popup is closed.

            SwingUtilities.invokeLater(new Runnable(){

                @Override
                public void run() {

                    System.out.println("Focus Update!");
                    System.out.println(mainFrame.getFocusOwner().toString());
                    UpdatePosLabel();
                }
            });
            }
            @Override
            public void focusLost(FocusEvent e) {}
        });

        //Did have DocumentListener, but as I recall it broke something (Can't remember what :( ), I'll experiment with adding it again.

        JScrollPane textScrollPane = new JScrollPane(textArea,
                                        JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
                                            JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);

        textPanel.add(textScrollPane);


        posLabel = new JLabel(" ");
        UpdatePosLabel();

        panel.add(textPanel, BorderLayout.CENTER);
        panel.add(posLabel, BorderLayout.PAGE_END);


    }

    private void initMenu()
    {
        //MenuBar
        JMenuBar jmb = new JMenuBar();
        mainFrame.setJMenuBar(jmb);

        JMenu menuEdit = new JMenu("Edit");

        Action Paste = textArea.getActionMap().get(DefaultEditorKit.pasteAction);

        JMenuItem itemPaste = new JMenuItem(Paste);
        itemPaste.setText("Paste");
        menuEdit.add(itemPaste);

        JMenuItem itemSelectAll = new JMenuItem("Select All");
        itemSelectAll.addActionListener(new ActionListener()  //Could maybe be done better...
                {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        textArea.selectAll();
                    }
                });
        menuEdit.add(itemSelectAll);

        jmb.add(menuEdit);


        //PopupMenu
        JPopupMenu popupMenu = new JPopupMenu();

        JMenuItem itemPastePopup = new JMenuItem(Paste);
        itemPastePopup.setText("Paste");
        popupMenu.add(itemPastePopup);


        JMenuItem itemSelectAllPopup = new JMenuItem("Select All");
        itemSelectAllPopup.addActionListener(new ActionListener()  //Could maybe be done better...
                {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        textArea.selectAll();
                    }
                });
        popupMenu.add(itemSelectAllPopup);

        textArea.setComponentPopupMenu(popupMenu);

    }

    //Just updates the label.
    private void UpdatePosLabel()
    {
        int lineNum = 1;
        int columnNum = 0;
        try {

            int caretpos = textArea.getCaretPosition();
            lineNum = textArea.getLineOfOffset(caretpos);
            columnNum = caretpos - textArea.getLineStartOffset(lineNum);
            lineNum += 1;
        }
        catch(Exception ex){
            posLabel.setText("Ln " + "?" + ", Col " + "?");
        }
        posLabel.setText("Ln " + lineNum + ", Col " + columnNum);
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable(){
            @Override
            public void run() {
                try
                {
                    new GUI();
                }
                catch(Exception e)
                {
                    e.printStackTrace();
                }
            }
        });
    }

}

要复制:正常键入或粘贴,请注意底部位置的更新。右键单击以显示菜单,然后选择粘贴或全选。尝试再次键入,位置不会更新(CaretListener不会运行)。

To replicate: Type or paste as normal, note the position updating at the bottom. Right click to bring up the menu, then select Paste or Select All. Try typing again, the position doesn't update (CaretListener not getting run).

注意:此不会接缝成为焦点问题(尽管我很可能错了),如 mainFrame.getFocusOwner()。toString()接缝以显示JTextArea,而 popupMenu。 setFocusable(false); 无济于事。

Note: This does not seam to be a focus issue (though I could very well be wrong), as mainFrame.getFocusOwner().toString() seams to show the JTextArea when called, and popupMenu.setFocusable(false);doesn't help.

我已经坚持了一段时间,所以如果您能帮助解释一下我做错了,我该怎么办才能触发CaretListener,我将非常感激! :)

I've been stuck on this for awhile now, so if you could help explain what I'm doing wrong, and how I might go about getting the CaretListener to fire, I would really appreciate it! :)

感谢和庆祝者万圣节快乐!

Thanks, and Happy Halloween to those who celebrate it!

更新: JTextFields也会发生这种情况(毫不奇怪,但是我认为我还是会对其进行测试),并且删除JScrollPane不会对接缝产生任何影响。同样,在JTextArea的插入符号上调用setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE)没什么区别。

Update: This also happens with JTextFields (unsurprising, but thought i'd test it anyway), and removing the JScrollPane doesn't seam to have any effect. Calling setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE) on the JTextArea's Caret likewise makes no difference.

更新2:我想我找到了解决方案(请参阅答案)...尽管我仍然不确定为什么会发生此问题。

Update 2: I think I found a solution (see my answer)... though I'm still not sure why this problem was happening.

推荐答案

好吧,我想我找到了一个解决方案……如果有人遇到此问题,我会在此处发布。
我最终要做的是直接在JTextArea的插入符号上设置一个ChangeListener:

Ok, I think I found a solution... I'll post it here in case anyone else experiences this. What I ended up doing is setting a ChangeListener directly on the JTextArea's Caret:

textArea.getCaret().addChangeListener(new ChangeListener(){     //Seams to work!

            @Override
            public void stateChanged(ChangeEvent e) {

                SwingUtilities.invokeLater(new Runnable(){

                    @Override
                    public void run() {

                        //Do stuff

                    }
                });
            }
        });

由于某种原因,即使JTextArea上的CaretListener没有打开,此接缝也会被触发。我不确定为什么会这样,所以我可能会对此发布另一个问题。

For some reason, this seams to get fired even when the CaretListener on the JTextArea doesn't. I'm not sure why this would happen though, so I might post another question regarding that.

希望这对可能遇到相同问题的人有所帮助。

Hope this helps anyone who might have the same issue.

这篇关于Java Swing:显示JPopupMenu后,插入符侦听器停止触发的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆