テキストエディタアプリケーションを作成していて、JPopupMenuが表示された後にテキストが入力または貼り付けられたときに私のCaretListenerが起動しないという問題が発生しました。Java Swing:JPopupMenuが表示された後、キャレットリスナーが起動しなくなる
私はこれが正常に動作し、 "runThisMethod()" いつでもキャレット移動(キーを押して、テキストの選択、など...)を呼び出し
textArea.addCaretListener(new CaretListener() {
public void caretUpdate(CaretEvent e) {
runThisMethod();
}
});
でJTextAreaのにCaretListenerを追加しました。私のアプリケーションでは、JMenuBarを持っていて、さらにtextArea.setComponentPopupMenu(popupMenu);
を使ってJPopupMenuを追加しました。
私の問題は、ポップアップが閉じられる(メニュー項目を選択するか、または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のを表示するよう、(私は非常によく間違っている可能性が)フォーカスの問題ではない継ぎ目を行い、そしてpopupMenu.setFocusable(false);
は役立ちません。
これはしばらくの間停滞しています。私が間違っていることを説明するのに役立ち、CaretListenerをどのように起動させるかについては、本当に感謝しています。 :)
ありがとう、そしてハッピーハロウィンを祝う人に!
更新:また、これは(驚くが、私はとにかくそれをテストしようと思いました)のJTextFieldで発生し、JScrollPaneのを削除しても、効果があると縫い目はありません。 JTextAreaのCaretでsetUpdatePolicy(DefaultCaret.ALWAYS_UPDATE)を呼び出すと同様に違いはありません。
更新2:私は解決策を見つけたと思う(私の答えを参照)...しかし、私はまだこの問題が起こっていたかどうかはわかりません。
は、JTextComponentなどのリスナーを使用します(例:DocListener ...)。KeyListenerの代わりに – mKorbel
が@camickrの答えと同じように見える – mKorbel
フォーカスからのイベント内のコードは、invokeLater(フォーカスはJTextComponentと一緒に)に囲まれる必要があります – mKorbel