4

Background

My window is a java.awt.Frame, and inside of the Frame are two Panels (java.awt.Panel). I'm trying to make it so that the window handles buttons I press.

Try Number 1

I tried using a KeyListener, making the Frame implement the KeyListener. I added the KeyListener to the Frame, but the KeyListener functions didn't do anything when I pressed keys. (I tried printing with System.out.println().)

Try Number 2

I tried following this tutorial: http://tips4java.wordpress.com/2008/10/10/key-bindings/ . Here is the my attempt to handle pressing the SPACEBAR:

public void registerActions(){                                  //01
  Action myAction = new AbstractAction(){                       //02
    @Override                                                   //03
    public void actionPerformed(ActionEvent e) {                //04
      System.out.println("GREAT SUCCESS!");                     //05
    }                                                           //06
  };                                                            //07
  KeyStroke key = KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0); //08
  component.getInputMap().put(key, "myAction");                 //09
  component.getActionMap().put("myAction", myAction);           //10
}                                                               //11

The main problem is that I don't know what 'component' should be in lines 09 & 10, because my application does not have any JComponents.

My Question

Is there a way to do this without using swing components? Or is there another way to handle key presses?

mKorbel
  • 109,525
  • 20
  • 134
  • 319
NoBrainer
  • 5,853
  • 1
  • 27
  • 27
  • I'm curious about why you are not using Swing. Is it a legacy app? – davidbuzatto Sep 15 '12 at 05:25
  • I'm eventually making my program multithreaded, and I've heard that Swing isn't thread-safe. – NoBrainer Sep 15 '12 at 05:32
  • 4
    The thread safety is related to GUI updates, not that you can't use threads. There are lots of sources over the web that you can read to learn how to deal with threads when using Swing. – davidbuzatto Sep 15 '12 at 05:35
  • Trying using either the root pane or content pane. Remember, child components may consume key stokes before they reach you – MadProgrammer Sep 15 '12 at 06:04
  • Unfortunately, getContentPane() and getRootPane() are not available for java.awt.Frame or java.awt.Panel. – NoBrainer Sep 15 '12 at 06:23
  • Simply switch to Swing for your UI instead of AWT. You shouldn't use multiple `Thread`s to update an AWT UI either – Robin Sep 15 '12 at 10:22
  • @NoBrainer Why are you using AWT classes? – MadProgrammer Sep 15 '12 at 21:49
  • @MadProgrammer: I'm messing around with the player moving a square around the screen then pressing spacebar to make a circle go in all directions and be able to detect when the circle hits things. How would you suggest I do this differently? – NoBrainer Sep 16 '12 at 01:14
  • 1
    @nobrainer I'd switch to using a JPanel instead. Apply the KeyBindings to it directly and use it's paintCompont method to paint your graphics. You'll need to be sure to set the panel as focusable – MadProgrammer Sep 16 '12 at 02:55

2 Answers2

4

I found that I could do this with an AWTEventListener.

public class MyFrame extends Frame implements AWTEventListener {

  ...

  public MyFrame(String title){
    super(title);
    ...
    this.getToolkit().addAWTEventListener(this, AWTEvent.KEY_EVENT_MASK);
  }

  @Override
  public void eventDispatched(AWTEvent event) {
    if(event instanceof KeyEvent){
      KeyEvent key = (KeyEvent)event;
      if(key.getID()==KeyEvent.KEY_PRESSED){ //Handle key presses
        System.out.println(key.getKeyChar());
        //TODO: do something with the key press
        key.consume();
      }
    }
  }
}
NoBrainer
  • 5,853
  • 1
  • 27
  • 27
4

Okay, here's an example using a JPanel.

I created a Frame, set it's layout to BorderLayout added to the KeyPane to it and voila...

public class KeyPane extends JPanel {

    private Timer paintTimer;
    private MouseFocusHandler mouseFocusHandler;

    private boolean spaceOn = false;
    private int yPos = 0;
    private int direction = 2;

    private Rectangle blob = new Rectangle(0, 0, 10, 10);

    public KeyPane() {

        setFocusable(true);

        InputMap im = getInputMap(WHEN_FOCUSED);
        ActionMap am = getActionMap();

        im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "Space");
        am.put("Space", new SpaceAction());

        setPreferredSize(new Dimension(100, 100));

        getPaintTimer().setCoalesce(false);
        getPaintTimer().start();

    }

    @Override
    public void addNotify() {
        super.addNotify();
        requestFocusInWindow();
        addMouseListener(getMouseFocusHandler());
        getPaintTimer().start();
    }

    @Override
    public void removeNotify() {
        removeMouseListener(getMouseFocusHandler());
        getPaintTimer().stop();
        super.removeNotify();
    }

    protected Timer getPaintTimer() {
        if (paintTimer == null) {
            paintTimer = new Timer(40, new RepaintAction());
            paintTimer.setRepeats(true);
            paintTimer.setCoalesce(true);
        }
        return paintTimer;
    }

    protected MouseFocusHandler getMouseFocusHandler() {
        if (mouseFocusHandler == null) {
            mouseFocusHandler = new MouseFocusHandler();
        }
        return mouseFocusHandler;
    }

    @Override
    protected void paintComponent(Graphics g) {

        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g.create();

        int width = getWidth() - 1;
        int height = getHeight() - 1;

        g2d.setColor(Color.GREEN);

        blob.x = (width - blob.width) / 2;

        System.out.println(blob);
        g2d.fill(blob);

        if (spaceOn) {
            g2d.setFont(UIManager.getFont("Label.font").deriveFont(24f));
            FontMetrics fm = g2d.getFontMetrics();

            String spaceIsOn = "Space On";

            int x = (width - fm.stringWidth(spaceIsOn)) / 2;
            int y = ((height - fm.getHeight()) / 2) + fm.getAscent();

            g2d.drawString(spaceIsOn, x, y);
        }

        g2d.dispose();

    }

    protected class MouseFocusHandler extends MouseAdapter {

        @Override
        public void mouseClicked(MouseEvent e) {
            requestFocusInWindow();
        }

    }

    protected class RepaintAction implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            yPos += direction;
            blob.y = yPos;

            if (blob.y + blob.height > getHeight() - 1) {
                blob.y = getHeight() - 1 - blob.height;
                direction = -2;
            } else if (blob.y < 0) {
                blob.y = 0;
                direction = 2;
            }
            repaint();
        }

    }

    protected class SpaceAction extends AbstractAction {

        @Override
        public void actionPerformed(ActionEvent e) {
            spaceOn = !spaceOn;
            repaint();
        }

    }

}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • I'm going to test this out, and if it works better than my solution, then you got yourself a green check-mark, sir. – NoBrainer Sep 17 '12 at 03:17
  • Well, your solution works, but the moving box doesn't move smoothly. Rather, it blinks erratically. I'm going to mess around to see if I can make it go more smoothly, but this isn't really acceptable in my opinion. – NoBrainer Sep 17 '12 at 22:48
  • @NoBrainer It's an example of the keybindings, not the animation :P – MadProgrammer Sep 17 '12 at 22:52
  • @NoBrainer Don't forget, you're question was about key bindings, not animation ;) – MadProgrammer Sep 17 '12 at 23:01
  • Touche. However, I'm currently comparing awt with swing to see which I want to use. So animation isn't a part of the question, but it is a part of my goal. – NoBrainer Sep 18 '12 at 01:31
  • @NoBrainer Mondays are awesome ;) - In my general opinion, I think you will find Swing a much for flexible solution when it comes to animation. You could pull the source code I did for this http://stackoverflow.com/questions/12438619/how-to-notify-multithread-event-on-swing-game/12454356#12454356 question as a really simply example of a game engine. – MadProgrammer Sep 18 '12 at 01:40
  • @NoBrainer You may also want to take a look at [TimingFramework](http://java.net/projects/timingframework) and [Trident](http://kenai.com/projects/trident/pages/Home) animation frameworks – MadProgrammer Sep 18 '12 at 01:42