Did I structure this key listener properly?

2019-03-06 15:24发布

问题:

I'm asking because I want the addShot() method in my GamePanel class to be called when the user hits enter," this initializes a shot object representing a missile fired from a ship, but it doesn't do anything. Is there a visibility issue here, or did I just structure the key event and listener relationship wrong? I'm only posting relevant code, but I can post the rest if necessary.

Here's the code:

public static class GameTest extends JFrame {

    private static final int WINDOW_WIDTH = 800;
    private static final int WINDOW_HEIGHT = 500;
    public GamePanel gamePanel;


    public GameTest() throws IOException {
        super("Deep Fried Freedom");
        setResizable(false);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
        setLayout(new BorderLayout());
        gamePanel = new GamePanel();
        add(gamePanel);
        center(this);
        setVisible(true);
        this.addKeyListener(new aKeyListener());
        this.setFocusable(true);

    } // end constructor

    public void center(JFrame frame) {
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        Point center = ge.getCenterPoint();

        int w = frame.getWidth();
        int h = frame.getHeight();

        int x = center.x - w / 2, y = center.y - h / 2;
        frame.setBounds(x, y, w, h);
        frame.validate();
    }//end of center method  

    public class aKeyListener implements KeyListener {

        @Override
        public void keyTyped(KeyEvent e) {
        }//end empty keyTyped method

        @Override
        public void keyPressed(KeyEvent e) {
            switch (e.getKeyCode()) {
                case KeyEvent.VK_A:
                    Launcher.lRun = -20;
                    gamePanel.move(gamePanel);
                    break;
                case KeyEvent.VK_D:
                    Launcher.lRun = 20;
                    gamePanel.move(gamePanel);
                    break;
                case KeyEvent.VK_ENTER:
                        gamePanel.addShot();
                    break;
                default:
                    Launcher.lRun = 0;
            }

        }//end keyPressed method

        @Override
        public void keyReleased(KeyEvent e) {
        }//end empty keyReleased method

    }//end aKeyListener class

}//end GameTest class

回答1:

  1. Yours is likely a focus issue -- something in the GamePanel stealing focus perhaps, a very common problem with KeyListeners.
  2. General advice: favor using Key Bindings, not a KeyListener for this type of need.

Useful links:

  • Swing Resources
  • Please check my second example program in my answer to a StackOverflow question here.
  • Overall Swing Tutorials
  • Key Bindings
  • Swing Timer
  • Concurrency in Swing

Edit
For example:

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

import javax.swing.*;

public class GameTest extends JFrame {

   private static final int WINDOW_WIDTH = 800;
   private static final int WINDOW_HEIGHT = 500;
   public GamePanel gamePanel;

   public GameTest() {
      super("Deep Fried Freedom");
      setResizable(false);
      // setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      // setSize(WINDOW_WIDTH, WINDOW_HEIGHT); // never do this
      gamePanel = new GamePanel();
      setUpKeyBinding(gamePanel);
      add(gamePanel);
      pack();
      setLocationRelativeTo(null); // to center!
      setVisible(true);
   }

   @Override
   public Dimension getPreferredSize() {
      return new Dimension(WINDOW_WIDTH, WINDOW_HEIGHT);
   }

   private void setUpKeyBinding(GamePanel gamePanel2) {

      // only need window to have focus 
      int condition = JComponent.WHEN_IN_FOCUSED_WINDOW; 

      // get the GamePanel's InputMap and ActionMap as these will be used to set
      // up our Key Bindings
      InputMap inputMap = gamePanel2.getInputMap(condition);
      ActionMap actionMap = gamePanel2.getActionMap();

      // bind the A key to move back 20 pixels
      KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_A, 0);
      inputMap.put(keyStroke, keyStroke.toString());
      actionMap.put(keyStroke.toString(), new MoveAction(gamePanel2, -20));

      // bind the D key to move forward 20 pixels
      keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_D, 0);
      inputMap.put(keyStroke, keyStroke.toString());
      actionMap.put(keyStroke.toString(), new MoveAction(gamePanel2, 20));

      // bind the ENTER key
      keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
      inputMap.put(keyStroke, keyStroke.toString());
      actionMap.put(keyStroke.toString(), new EnterAction(gamePanel2));

   }

   public static void main(String[] args) {
      new GameTest();
   }

   // our Action classes that will bound to key strokes
   private class MoveAction extends AbstractAction {
      private GamePanel gamePanel2;
      private int distance;

      public MoveAction(GamePanel gamePanel2, int distance) {
         this.gamePanel2 = gamePanel2;
         this.distance = distance;
      }

      @Override
      public void actionPerformed(ActionEvent e) {
         gamePanel2.moveItem(distance);
      }
   }

   private class EnterAction extends AbstractAction {
      private GamePanel gamePanel2;

      public EnterAction(GamePanel gamePanel2) {
         this.gamePanel2 = gamePanel2;
      }

      @Override
      public void actionPerformed(ActionEvent e) {
         gamePanel2.addShot();
      }
   }

}

// a trivial GamePanel class to just show that everything is working as expected
class GamePanel extends JPanel {

   public void moveItem(int i) {
      // your moveItem method will instead actually move something, a distance of i
      System.out.println("Move Item Method distance: " + i);
   }

   public void addShot() {
      // your addShot method will actually shoot
      System.out.println("Add Shot Method");
   }

}