Java Swing:显示JPopupMenu后,插入符号监听器停止触发

时间:2016-10-31 09:47:09

标签: java swing editor caret

我正在编写一个文本编辑器应用程序,而且在显示JPopupMenu后输入或粘贴文本时,我的CaretListener遇到了问题,我没有被解雇。

我已将CaretListener添加到带有

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

这很好用,并调用" runThisMethod()"插入符号移动时(按键,文本选择等)。在我的应用程序中,我有一个JMenuBar,还有一个使用textArea.setComponentPopupMenu(popupMenu);添加的JPopupMenu。

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

以下是演示此问题的代码示例(很抱歉):

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没有运行)。

注意:这个接缝是一个焦点问题(尽管我很可能是错的),因为mainFrame.getFocusOwner().toString()接缝在调用时显示JTextArea,{{1}没有帮助。

我现在已经坚持了一段时间,所以如果你能帮我解释一下我做错了什么,以及如何让CaretListener解雇,我真的很感激! :)

谢谢,并祝贺那些庆祝它的万圣节快乐!

更新:这也适用于JTextFields(不足为奇,但我认为无论如何都要测试它),并且删除JScrollPane并不会产生任何影响。在JTextArea的Caret上调用setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE)同样没有任何区别。

更新2:我想我找到了一个解决方案(请参阅我的回答)......虽然我还不确定为什么会出现这个问题。

1 个答案:

答案 0 :(得分:1)

好的,我想我找到了一个解决方案......我会在这里发布,以防其他人遇到这个问题。 我最终做的是直接在JTextArea的Caret上设置ChangeListener:

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没有触发,这个接缝也会被触发。我不确定为什么会这样,所以我可能会发布另一个问题。

希望这有助于任何可能遇到同样问题的人。