Java Swing:Caret-listener 在 JPopupMenu 显示后停止触发
Java Swing: Caret-listener stops firing after JPopupMenu is shown
我正在制作一个文本编辑器应用程序,我遇到了一个问题,即在显示 JPopupMenu 后键入或粘贴文本时我的 CaretListener 没有被触发。
我已将 CaretListener 添加到带有
的 JTextArea
textArea.addCaretListener(new CaretListener() {
public void caretUpdate(CaretEvent e) {
runThisMethod();
}
});
这很好用,只要插入符号移动(按键、文本 selection 等...),就会调用 "runThisMethod()"。在我的应用程序中,我有一个 JMenuBar,还有一个使用 textArea.setComponentPopupMenu(popupMenu);
.
添加的 JPopupMenu
我的问题是每当弹出窗口关闭时(通过 select 菜单项,或通过单击 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();
}
}
});
}
}
要复制: 正常输入或粘贴,注意底部的位置更新。右键单击以调出菜单,然后 select 粘贴或 Select 全部。再次尝试输入,位置没有更新(CaretListener 没有得到 运行)。
注意:不会接缝成为焦点问题(尽管我很可能是错的),因为mainFrame.getFocusOwner().toString()
接缝在调用时显示 JTextArea, popupMenu.setFocusable(false);
没有帮助。
我已经坚持了一段时间,所以如果你能帮助解释我做错了什么,以及我如何让 CaretListener 触发,我将非常感激! :)
谢谢,万圣节快乐!
更新: JTextFields 也会发生这种情况(不足为奇,但我想我还是会测试它),删除 JScrollPane 不会产生任何效果。在 JTextArea 的插入符号上调用 setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE) 同样没有任何区别。
更新 2: 我想我找到了解决方案(请参阅我的回答)...虽然我仍然不确定为什么会出现此问题。
好的,我想我找到了解决方案...我将 post 放在这里以防其他人遇到此问题。
我最终做的是直接在 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 没有触发,这个接缝也会被触发。我不确定为什么会发生这种情况,所以我可能 post 关于那个的另一个问题。
希望这对可能遇到相同问题的任何人有所帮助。
我正在制作一个文本编辑器应用程序,我遇到了一个问题,即在显示 JPopupMenu 后键入或粘贴文本时我的 CaretListener 没有被触发。
我已将 CaretListener 添加到带有
的 JTextAreatextArea.addCaretListener(new CaretListener() {
public void caretUpdate(CaretEvent e) {
runThisMethod();
}
});
这很好用,只要插入符号移动(按键、文本 selection 等...),就会调用 "runThisMethod()"。在我的应用程序中,我有一个 JMenuBar,还有一个使用 textArea.setComponentPopupMenu(popupMenu);
.
我的问题是每当弹出窗口关闭时(通过 select 菜单项,或通过单击 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();
}
}
});
}
}
要复制: 正常输入或粘贴,注意底部的位置更新。右键单击以调出菜单,然后 select 粘贴或 Select 全部。再次尝试输入,位置没有更新(CaretListener 没有得到 运行)。
注意:不会接缝成为焦点问题(尽管我很可能是错的),因为mainFrame.getFocusOwner().toString()
接缝在调用时显示 JTextArea, popupMenu.setFocusable(false);
没有帮助。
我已经坚持了一段时间,所以如果你能帮助解释我做错了什么,以及我如何让 CaretListener 触发,我将非常感激! :)
谢谢,万圣节快乐!
更新: JTextFields 也会发生这种情况(不足为奇,但我想我还是会测试它),删除 JScrollPane 不会产生任何效果。在 JTextArea 的插入符号上调用 setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE) 同样没有任何区别。
更新 2: 我想我找到了解决方案(请参阅我的回答)...虽然我仍然不确定为什么会出现此问题。
好的,我想我找到了解决方案...我将 post 放在这里以防其他人遇到此问题。 我最终做的是直接在 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 没有触发,这个接缝也会被触发。我不确定为什么会发生这种情况,所以我可能 post 关于那个的另一个问题。
希望这对可能遇到相同问题的任何人有所帮助。