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

2024-01-30

我正在制作一个文本编辑器应用程序,并且遇到了一个问题:在显示 JPopupMenu 后键入或粘贴文本时,我的 CaretListener 不会被触发。

我已将 CaretListener 添加到 JTextArea

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

这工作得很好,只要插入符号移动(按键、文本选择等),就会调用“runThisMethod()”。在我的应用程序中,我有一个 JMenuBar,还有一个使用添加的 JPopupMenutextArea.setComponentPopupMenu(popupMenu);.

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

这是演示该问题的代码示例(抱歉,篇幅较长):

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

注:这个does not接缝是一个焦点问题(尽管我很可能是错的),因为mainFrame.getFocusOwner().toString()调用时接缝显示 JTextArea,并且popupMenu.setFocusable(false);没有帮助。

我已经被困在这个问题上有一段时间了,所以如果你能帮助解释我做错了什么,以及我如何让 CaretListener 启动,我将非常感激! :)

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

Update:JTextFields 也会发生这种情况(毫不奇怪,但我想无论如何都会测试它),并且删除 JScrollPane 似乎不会产生任何效果。在 JTextArea 的 Caret 上调用 setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE) 同样没有什么区别。

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


好吧,我想我找到了解决方案......我会将其发布在这里,以防其他人遇到这种情况。 我最终做的是直接在 JTextArea 的插入符上设置 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 没有被触发,这个接缝也会被触发。我不确定为什么会发生这种情况,所以我可能会就此发布另一个问题。

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java Swing:显示 JPopupMenu 后插入符侦听器停止触发 的相关文章

随机推荐

  • 访问 Spark RDD 时闭包中局部变量的使用

    我有一个关于访问 Spark RDD 时闭包中局部变量的使用的问题 我想解决的问题如下 我有一个应该读入 RDD 的文本文件列表 但是 首先我需要向从单个文本文件创建的 RDD 添加附加信息 此附加信息是从文件名中提取的 然后 使用 uni
  • 将 ShapeRenderer.begin/end 嵌套在 SpriteBatch.begin/end 中

    是否可以使用绘制形状ShapeRenderer之间SpriteBatch begin and end calls 我已经尝试过 但没有结果 只绘制了 SpriteBatch 纹理 场景中没有形状 示例代码如下 shapeRenderer b
  • Objective-C 接口 - 声明变量与仅声明属性?

    在 Obj c 中 在 interface 中声明变量时 接口 NSObject 我的对象 我的对象 property 不安全 非原子 MyObject myObject 对比 仅将其声明为属性 接口 NSObject property 不
  • 通过 HTTP 回调函数进行 Google 地理编码?

    我想使用谷歌地理编码via HTTP在我的 AJAX Web 应用程序中将城市名称转换为经度和纬度的功能 但是 HTTP 地理编码器功能似乎不存在回调函数 http code google com apis maps documentati
  • 无法将操作提供者强制转换为共享操作提供者

    下面是我的活动的代码 import android app Activity import android os Bundle import android support v7 widget ShareActionProvider imp
  • Clojure 协议与 Scala 结构类型

    看完后里奇 希基的采访 http www infoq com interviews hickey clojure protocols on 协议 http clojure org protocols在 Clojure 1 2 中 对 Clo
  • 线程无异常地死亡

    我的一些工作线程遇到问题 我在线程的 run 方法中添加了一个包罗万象的异常语句 如下所示 try Runs the worker process which is a state machine while self set exitco
  • 为什么 ListView 项目不会增长以包裹其内容?

    我有一个相当复杂的 ListView 具有可变的列表项高度 在某些情况下 我需要在列表项中显示一个附加视图 该视图默认是隐藏的 View GONE 通过启用它 View VISIBLE 列表项的高度会增加 或者至少应该如此 问题 即使我将项
  • 如何将文件夹结构复制到另一个目录下?

    我有一些与复制文件夹结构相关的问题 事实上 我需要将pdf文件转换为文本文件 因此 我导入 pdf 的位置有这样的文件夹结构 D f subfolder1 subfolder2 a pdf 我想在 下创建确切的文件夹结构D g subfol
  • 在 Hibernate 中运行时急切加载整个对象图

    在说出 指定查询中的获取类型 之类的内容之前 请先阅读以下内容 那不是我所追求的 我正在寻找一种方法来急切加载完整的对象图 对象 它的所有子对象以及它们的所有子对象等等 I do not想要枚举要加载的所有属性 直到运行时我才认识它们 N
  • Linux中如何保护进程间共享的内存

    在 Linux 或其他现代操作系统中 每个进程的内存都受到保护 因此一个进程中的疯狂写入不会导致任何其他进程崩溃 现在假设我们在进程 A 和进程 B 之间共享内存 现在假设 由于软错误 进程 A 无意中向该内存区域写入了一些内容 鉴于进程
  • 如何在 React 中呈现未定义状态的数据?

    我正在 componentDidMount 内获取数据 但在初始渲染期间我未定义 然后再次渲染发生 并且在此期间状态变量被填充 现在 当它不是未定义的并且在填充之后 我想对其进行解构并在我的组件内显示数据 注意 getProjectDeta
  • C++ 中的变量作用域?

    在 C 中 main 中声明的任何变量都可以在整个 main 中使用 对吗 我的意思是 如果变量是在 try 循环中声明的 它们仍然可以在整个 main 中访问吗 因为我在 main 的 try 循环中声明了几个变量 但是如果我在 try
  • 如何获取字典中键的ReadOnlyCollection

    我的课程包含一个Dictionary
  • python time.strftime %z 始终为零而不是时区偏移

    gt gt gt import time gt gt gt t 1440935442 gt gt gt time strftime Y m d H M S z time gmtime t 2015 08 30 11 50 42 0000 g
  • 按实体名称和上次修改日期搜索

    我在 RavenDb 中存储了许多命令 它们都实现了 ICommand 我希望能够搜索上次修改的元数据和 Raven Entity Name 我目前正在对每个命令进行多重映射 如下所示 public class CommandAuditSe
  • 将 PHP 日期范围转换为 MYSQL 单个日期

    我有一个可用性日历 其中我当前正在逐个添加日期 并使用 mysql 查询来确定是否存在具有特定日期的行 并将当天的类别更改为 已预订 红色 我想在我的表单中输入一个范围 并通过 php 或 mysql 将其处理为多个单独的日期 我的日期格式
  • 使用 TensorFlow Dataset API 和 flat_map 的并行线程

    我正在将 TensorFlow 代码从旧的队列接口更改为新的数据集API https www tensorflow org api docs python tf data Dataset 使用旧界面我可以指定num threads论证tf
  • Powershell 检查本地管理员凭据

    我正在尝试运行一个需要管理员输入才能处理某些事情的脚本 我不想让脚本运行失败 而是尝试捕获错误并将其扔回到凭据中 但我找不到可以将本地管理凭据传递给陷阱的命令 有人有什么可能有用的东西吗 我发现很多都会检查域凭据 但这是一个本地管理员帐户
  • Java Swing:显示 JPopupMenu 后插入符侦听器停止触发

    我正在制作一个文本编辑器应用程序 并且遇到了一个问题 在显示 JPopupMenu 后键入或粘贴文本时 我的 CaretListener 不会被触发 我已将 CaretListener 添加到 JTextArea textArea addC