2016-10-31 13 views
2

テキストエディタアプリケーションを作成していて、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:私は解決策を見つけたと思う(私の答えを参照)...しかし、私はまだこの問題が起こっていたかどうかはわかりません。

+1

は、JTextComponentなどのリスナーを使用します(例:DocListener ...)。KeyListenerの代わりに – mKorbel

+0

が@camickrの答えと同じように見える – mKorbel

+0

フォーカスからのイベント内のコードは、invokeLater(フォーカスはJTextComponentと一緒に)に囲まれる必要があります – mKorbel

答えて

1

私は解決策を見つけたと思います...他の誰かがこれを経験した場合は、ここに投稿します。私は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がない場合でも、解雇する縫い目。私はなぜこれが起こるかわからないので、私はそれについて別の質問を掲示するかもしれない。

これは、同じ問題を抱えている可能性のある人に役立ちます。

関連する問題