Can't seem to get keyboard focus properly

Hi folks.

I’m trying to create a JPanel and get keyboard events from that panel. i’ve been reading through the docs and whatnot, but i can’t seem to get it setup properly. Can anyone help?

I’m including three sets of the same code. The desired outcome is that when you run the app, every time you hit a key, it a message should appear in the console.

This first code example doesn’t work properly on my WinXP machine (sun java 1.4.1_02), or my MacOS machine (apple java 1.4.2_05). When you run the app, and hit any key, no output appears on the console:


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class KeyListenerTest0 extends JPanel implements Runnable {

 private Thread m_subThread = null;

 public static void main ( String[] argv ) {
  final Frame f = new Frame ( "KeyListenerTest0 1" );
  f.addWindowListener ( new WindowAdapter () {
   public void windowClosing ( WindowEvent e ) {
    f.dispose ();
    System.exit ( 0 );
   }
  } );

  f.add ( new KeyListenerTest0 () );
  f.setSize ( 220, 220 );
  f.setVisible ( true );
 }

 public void run () {
  Thread.currentThread ().setPriority ( Thread.MIN_PRIORITY );
  while ( true ) {
   try {
    Thread.sleep ( 500 );
   } catch ( Exception e ) {
   }
   repaint ();
  }
 }

 public KeyListenerTest0 () {
  setBackground ( Color.white );
  setLayout ( new BorderLayout () );

  //  Add a key listener which will just dump out info.
  addKeyListener ( new KeyListener () {
   public void keyPressed ( KeyEvent ke ) { System.out.println ( "Key Pressed: " + ke.getKeyChar () ); }
   public void keyReleased ( KeyEvent ke ) { System.out.println ( "Key Released: " + ke.getKeyChar () ); }
   public void keyTyped ( KeyEvent ke ) { System.out.println ( "Key Typed: " + ke.getKeyChar () ); }
  } );
  //  Print out focus info
  getFocus ( "First Try" );

  m_subThread = new Thread ( this );
  m_subThread.start ();
 }

 private void getFocus ( String label ) {
  System.out.println ( "Trying to get focus: '" + label + "'" );
  System.out.println ( "  isFocusable: " + isFocusable () );
  System.out.println ( "  hasFocus: " + hasFocus () );
  if ( hasFocus () == false ) {
   boolean b = requestFocusInWindow ();
   System.out.println ( "  requestFocusInWindow returns: " + b );
  }
  System.out.println ( "  hasFocus: " + hasFocus () );
  KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager ();
  Component c = kfm.getFocusOwner ();
  System.out.println ( "  Who has focus?: " + c );
  System.out.println ( "Done trying to get focus for: '" + label + "'" );
  System.out.println();
}

 public void paintComponent ( Graphics g ) {
  Graphics2D g2 = (Graphics2D) g;
  g2.setRenderingHint ( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
  g2.setPaint ( Color.orange );
  g2.fillOval ( 100, 100, 100, 100 );
 }
}

This second example works on my WinXP machine, but not on my mac. The only thing different is that I put a getFocus() call in the run() method. I’m pretty sure that this is not the way i’m supposed to do this…


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class KeyListenerTest1 extends JPanel implements Runnable {

 private Thread m_subThread = null;

 public static void main ( String[] argv ) {
  final Frame f = new Frame ( "KeyListenerTest1 1" );
  f.addWindowListener ( new WindowAdapter () {
   public void windowClosing ( WindowEvent e ) {
    f.dispose ();
    System.exit ( 0 );
   }
  } );

  f.add ( new KeyListenerTest1 () );
  f.setSize ( 220, 220 );
  f.setVisible ( true );
 }

 public void run () {
  Thread.currentThread ().setPriority ( Thread.MIN_PRIORITY );
  //  Try to get focus now.  This works on WinXP, but not MacOS.
  getFocus ( "Second try from 'run'" );
  while ( true ) {
   try {
    Thread.sleep ( 500 );
   } catch ( Exception e ) {
   }
   repaint ();
  }
 }

 public KeyListenerTest1 () {
  setBackground ( Color.white );
  setLayout ( new BorderLayout () );

  //  Add a key listener which will just dump out info.
  addKeyListener ( new KeyListener () {
   public void keyPressed ( KeyEvent ke ) { System.out.println ( "Key Pressed: " + ke.getKeyChar () ); }
   public void keyReleased ( KeyEvent ke ) { System.out.println ( "Key Released: " + ke.getKeyChar () ); }
   public void keyTyped ( KeyEvent ke ) { System.out.println ( "Key Typed: " + ke.getKeyChar () ); }
  } );
  //  Print out focus info
  getFocus ( "First Try" );

  m_subThread = new Thread ( this );
  m_subThread.start ();
 }

 private void getFocus ( String label ) {
  System.out.println ( "Trying to get focus: '" + label + "'" );
  System.out.println ( "  isFocusable: " + isFocusable () );
  System.out.println ( "  hasFocus: " + hasFocus () );
  if ( hasFocus () == false ) {
   boolean b = requestFocusInWindow ();
   System.out.println ( "  requestFocusInWindow returns: " + b );
  }
  System.out.println ( "  hasFocus: " + hasFocus () );
  KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager ();
  Component c = kfm.getFocusOwner ();
  System.out.println ( "  Who has focus?: " + c );
  System.out.println ( "Done trying to get focus for: '" + label + "'" );
  System.out.println();
}

 public void paintComponent ( Graphics g ) {
  Graphics2D g2 = (Graphics2D) g;
  g2.setRenderingHint ( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
  g2.setPaint ( Color.orange );
  g2.fillOval ( 100, 100, 100, 100 );
 }
}

This third example works on both the WinXP machine and Mac. I have now placed a getFocus() call in the paintComponent call. I am darn near certain that this is not the way to go. I must be missing something simple.


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class KeyListenerTest2 extends JPanel implements Runnable {

 private Thread m_subThread = null;

 public static void main ( String[] argv ) {
  final Frame f = new Frame ( "KeyListenerTest2 1" );
  f.addWindowListener ( new WindowAdapter () {
   public void windowClosing ( WindowEvent e ) {
    f.dispose ();
    System.exit ( 0 );
   }
  } );

  f.add ( new KeyListenerTest2 () );
  f.setSize ( 220, 220 );
  f.setVisible ( true );
 }

 public void run () {
  Thread.currentThread ().setPriority ( Thread.MIN_PRIORITY );
  //  Try to get focus now.  This works on WinXP, but not MacOS.
  getFocus ( "Second try from 'run'" );
  while ( true ) {
   try {
    Thread.sleep ( 500 );
   } catch ( Exception e ) {
   }
   repaint ();
  }
 }

 public KeyListenerTest2 () {
  setBackground ( Color.white );
  setLayout ( new BorderLayout () );

  //  Add a key listener which will just dump out info.
  addKeyListener ( new KeyListener () {
   public void keyPressed ( KeyEvent ke ) { System.out.println ( "Key Pressed: " + ke.getKeyChar () ); }
   public void keyReleased ( KeyEvent ke ) { System.out.println ( "Key Released: " + ke.getKeyChar () ); }
   public void keyTyped ( KeyEvent ke ) { System.out.println ( "Key Typed: " + ke.getKeyChar () ); }
  } );
  //  Print out focus info
  getFocus ( "First Try" );

  m_subThread = new Thread ( this );
  m_subThread.start ();
 }

 private void getFocus ( String label ) {
  System.out.println ( "Trying to get focus: '" + label + "'" );
  System.out.println ( "  isFocusable: " + isFocusable () );
  System.out.println ( "  hasFocus: " + hasFocus () );
  if ( hasFocus () == false ) {
   boolean b = requestFocusInWindow ();
   System.out.println ( "  requestFocusInWindow returns: " + b );
  }
  System.out.println ( "  hasFocus: " + hasFocus () );
  KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager ();
  Component c = kfm.getFocusOwner ();
  System.out.println ( "  Who has focus?: " + c );
  System.out.println ( "Done trying to get focus for: '" + label + "'" );
  System.out.println();
}

 public void paintComponent ( Graphics g ) {
  Graphics2D g2 = (Graphics2D) g;
  g2.setRenderingHint ( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
  g2.setPaint ( Color.orange );
  g2.fillOval ( 100, 100, 100, 100 );
  // try to get focus if we don't already have it.
  if ( hasFocus() == false ) {
   getFocus ( "Inline during paintComponent" );
  }
 }
}

Thanks for any help.

…alex…

getFocus() ??? I get the focus for the component by using the requestFocus() function. And then it works.

[quote]getFocus() ??? I get the focus for the component by using the requestFocus() function. And then it works.
[/quote]
getFocus() is a method of my own that i’m using to try to get the focus and also display some debugging information.

The javadoc for Component in the JDK1.4 says:

[quote]Because the focus behavior of this method is platform-dependent, developers are strongly encouraged to use requestFocusInWindow when possible.
[/quote]
Hence this is why i’m using requestFocusInWindow() in the getFocus method.

WHOOHOO! I think i figured it out.

Like i said in my original post, i was missing something.

Let me explain…(to solidify the topics in my head and also to help someone else who reads this thead later…) Here’s what i think is/was going on. Someone please correct me if i’m completely off my rocker…

[]First off, i didn’t understand the difference between the window having focus and the component having focus
[
]Secondly, I was trying to get the focus before the component was realized (before the event dispatch thread was started.

I first did some more debugging and found that when the appliacation is initially started up, the parent ApplicationFrame (a Frame subclass) had the focus. I wondered why that didn’t automatically translate into my component having focus since there was only one component in the frame.

I then went poking around the javadoc some more and found a link to the How to use the Focus Subsystem page in the Swing tutorial (http://java.sun.com/docs/books/tutorial/uiswing/misc/focus.html). On this page, it says:

From this, i was able to see the err in my ways.

the answer to the initial question i had (how to get the component to have focus when the app starts up) can be accomplished in either of these two ways. Although since i didn’t specify that i wanted the component to always have the focus when the window gets the focus, the latter is more applicable.

So, in the last version of the KeyListenerTest code the following changes were made:
[]removed the getFocus() method altogether and all calls to getFocus() as well.
[
]In the main() method, I added a call to pack(), as well as requestFocusInWindow() before i set the frame to visible:

 public static void main ( String[] argv ) {
  final Frame f = new Frame ( "KeyListenerTest3 1" );
  final KeyListenerTest3 klt = new KeyListenerTest3();

  f.addWindowListener ( new WindowAdapter () {
   public void windowClosing ( WindowEvent e ) {
    f.dispose ();
    System.exit ( 0 );
   }
  } );
 
  f.add ( klt );
  // Make sure the components are realized and the event dispatch queue thread starts
  f.pack();

  // set the size of the frame.
  f.setSize ( 220, 220 );

  // now tell the component to request focus in the window.
  klt.requestFocusInWindow();

  // when we now show the window, the KeyListenerTest3 component (along with 
  // it's KeyListener should have focus!  Yippee!
  f.setVisible ( true );
 }

The full listing follows:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
 
public class KeyListenerTest3 extends JPanel implements Runnable {
 
 private Thread m_subThread = null;
 
 public static void main ( String[] argv ) {
  final Frame f = new Frame ( "KeyListenerTest3 1" );
  final KeyListenerTest3 klt = new KeyListenerTest3();

  f.addWindowListener ( new WindowAdapter () {
   public void windowClosing ( WindowEvent e ) {
    f.dispose ();
    System.exit ( 0 );
   }
  } );
 
  f.add ( klt );
  // Make sure the components are realized and the event dispatch queue thread starts
  f.pack();

  // set the size of the frame.
  f.setSize ( 220, 220 );

  // now tell the component to request focus in the window.
  klt.requestFocusInWindow();

  // when we now show the window, the KeyListenerTest3 component (along with 
  // it's KeyListener should have focus!  Yippee!
  f.setVisible ( true );
 }

 
 public void run () {
  Thread.currentThread ().setPriority ( Thread.MIN_PRIORITY );
  while ( true ) {
   try {
    Thread.sleep ( 500 );
   } catch ( Exception e ) {
   }
   repaint ();
  }
 }
 
 public KeyListenerTest3 () {
  setBackground ( Color.white );
  setLayout ( new BorderLayout () );
 
  //  Add a key listener which will just dump out info.
  addKeyListener ( new KeyListener () {
   public void keyPressed ( KeyEvent ke ) { System.out.println ( "Key Pressed: " + ke.getKeyChar () ); }
   public void keyReleased ( KeyEvent ke ) { System.out.println ( "Key Released: " + ke.getKeyChar () ); }
   public void keyTyped ( KeyEvent ke ) { System.out.println ( "Key Typed: " + ke.getKeyChar () ); }
  } );
 
  m_subThread = new Thread ( this );
  m_subThread.start ();
 }

 public Dimension getMinimumSize() {
  return new Dimension ( 220, 200 );
 }
 
 public void paintComponent ( Graphics g ) {
  Graphics2D g2 = (Graphics2D) g;
  g2.setRenderingHint ( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
  g2.setPaint ( Color.orange );
  g2.fillOval ( 100, 100, 100, 100 );
 }
} 

This solution seems more right to me. I hope i’m correct. ;D

Thanks.

…alex…