Java KeyListener isn't detecting keyboard inpu

2019-01-20 10:54发布

I'm trying to modify this program so that it draws an image of a castle and I am able to scale this image using the up and down arrow keys. I can't manage to get the keylistener to work, the program runs but it isn't responding to key presses. Any help will be appreciated, thanks.

import java.awt.*;
import java.awt.geom.*;
import java.util.*;
import javax.swing.*;
import java.net.*;
import java.awt.event.*;

public class DrawImage extends JFrame implements KeyListener {
int scale = 1;
    private Image image;
    enter code here
    public static void main(String[] args) {
        new DrawImage();
    }

    public DrawImage() {
        super("DrawImage");
        setSize(600,600);
        setVisible(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Toolkit tk = Toolkit.getDefaultToolkit();
        image = tk.getImage(getURL("castle.png"));
        addKeyListener(this);
    }

    private URL getURL(String filename) {
        URL url = null;
        try {
            url = this.getClass().getResource(filename);
        }
        catch (Exception e) { }
        return url;
    }

    public void paint(Graphics g) {
        Graphics2D g2d = (Graphics2D) g;
        AffineTransform trans = new AffineTransform();
        trans.scale(scale, scale);
        System.out.println("scale: " + scale);
        g2d.setColor(Color.BLACK);
        g2d.fillRect(0, 0, getSize().width, getSize().height);
        g2d.setTransform(trans);
        g2d.drawImage(image, 0, 40, this);
        addKeyListener(this);
    }

    public void keyReleased(KeyEvent e) { }
    public void keyTyped(KeyEvent e) { }
    public void keyPressed(KeyEvent e) {
        int key = e.getKeyCode();
        if (key == KeyEvent.VK_UP) {
            scale++;
        }
        if (key == KeyEvent.VK_DOWN) {
            scale--;
        }
    }
}

1条回答
放我归山
2楼-- · 2019-01-20 11:11

I can't manage to get the keylistener to work, the program runs but it isn't responding to key presses.

Yours is a very common problem that is asked here frequently, and almost always the problem is due to lack of focus -- that the component being listened to does not have the current focus, and focus is essential for a KeyListener to work.

The short answer is to give the component that is being listened to the focus.

The longer better answer is to not use KeyListeners and instead to use Key Bindings for such a project.


Edit
Other problems:

  • In your code above, you're adding the KeyListener to a JFrame, and this shouldn't be done, even if you had good reason to use KeyListeners.
  • You're also drawing directly in the JFrame which in my opinion is an even bigger problem, since you risk unwanted side effects.
  • You're adding a listener inside of the paint method, an even bigger problem. The paint method should not be overridden (in general), and if you did have to override it, should never be used for program logic, for adding or removing listeners, or for doing any non-painting activities. It should be for painting and painting only.
  • Instead you should draw directly in a JPanel or JComponent.
  • Instead you should do the drawing in a paintComponent(Graphics g) override in your painting JPanel or JComponent.
  • Just as described above, this method should be for painting and painting only and should be as fast as possible.
  • You should call the super.paintComponent(g) inside of your paintComponent(Graphics g) override.
  • Again the drawing component should listen for key strokes using Key Bindings (tutorial here). The tutorial will explain why this distinction (KeyListener vs. Key Bindings) is important.

Edit 2
You never want to ignore exceptions as your code shows here since this is the programming equivalent of driving blind:

  try {
     url = this.getClass().getResource(filename);
  } catch (Exception e) {
  }

Hopefully this is not how your production code looks, that you only ignored the exceptions in your posted code for the sake of brevity, but understand for many of us seeing this is like hearing nails on a chalk-board.


Edit 3
More on KeyListeners vs. Key Bindings: Assuming that you got your code to work with a KeyListener, then assume that you added any other focusable components to your GUI and they somehow got the focus via user interaction, then your KeyBindings will no longer work. If you had done this correctly with Key Bindings, this would not be an issue.


Edit 4
You really want your scale field to be a double, not an int. And you really don't want to increment and decrement it, but rather you want to multiply it and divide it by some multiplier constant, say 1.2 as an example. You will also want to call repaint() whenever you change your scale.


Edit 5
Please check out two sample programs, the first, called DrawImagePanelKeyListener.java, uses a KeyListener, while the second, called DrawImagePanelKeyBindings, uses Key Bindings. They both should compile, run, and function as expected: when you press the up or down arrow keys, the displayed image shrinks and grows. The difference in their behavior though can be seen when the JButton both have is pressed. Press the button and see what happens to your key response. When the component that has a KeyListener loses focus, its KeyListener will stop working, but the same is not true for the component that uses Key Bindings.

A kludge that could get around this problem could be to prevent all other components from getting the focus, but that is not practical or desired with most GUI's.

DrawImagePanelKeyListener.java

import java.awt.*;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.io.IOException;
import java.net.*;
import java.awt.event.*;

@SuppressWarnings("serial")
public class DrawImagePanelKeyListener extends JPanel implements KeyListener {
   public static final String IMAGE_PATH = "https://duke.kenai.com/"
         + "nyanya/.Midsize/NyaNya.jpg.png";
   private static final double MULTIPLIER = 1.2;
   double scale = 1.0;
   private Image image;
   private Dimension initialSize = new Dimension(0, 0);

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            DrawImagePanelKeyListener drawImage = new DrawImagePanelKeyListener();
            drawImage.add(new JButton("Foo"));
            JFrame frame = new JFrame("Draw Image");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(drawImage);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
         }
      });
   }

   public DrawImagePanelKeyListener() {
      setFocusable(true);
      requestFocusInWindow();
      URL imgURL;
      try {
         imgURL = new URL(IMAGE_PATH);
         image = ImageIO.read(imgURL);
         initialSize = new Dimension(image.getWidth(this),
               image.getHeight(this));
         addKeyListener(this);
         setVisible(true);
      } catch (MalformedURLException e) {
         e.printStackTrace();
      } catch (IOException e) {
         e.printStackTrace();
      }
   }

   @Override
   public Dimension getPreferredSize() {
      return initialSize;
   }

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      g.setColor(Color.BLACK);
      g.fillRect(0, 0, getSize().width, getSize().height);
      Graphics2D g2d = (Graphics2D) g.create();
      g2d.scale(scale, scale);
      g2d.drawImage(image, 0, 0, this);
   }

   public void keyReleased(KeyEvent e) {
   }

   public void keyTyped(KeyEvent e) {
   }

   public void keyPressed(KeyEvent e) {
      int key = e.getKeyCode();
      if (key == KeyEvent.VK_UP) {
         scale *= MULTIPLIER;
      }
      if (key == KeyEvent.VK_DOWN) {
         scale /= MULTIPLIER;
      }
      repaint();
   }
}

DrawImagePanelKeyBindings.java

import java.awt.*;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.io.IOException;
import java.net.*;
import java.awt.event.*;

@SuppressWarnings("serial")
public class DrawImagePanelKeyBindings extends JPanel {
   public static final String IMAGE_PATH = "https://duke.kenai.com/"
         + "nyanya/.Midsize/NyaNya.jpg.png";
   private static final double MULTIPLIER = 1.2;
   double scale = 1.0;
   private Image image;
   private Dimension initialSize = new Dimension(0, 0);

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            DrawImagePanelKeyBindings drawImage = new DrawImagePanelKeyBindings();
            drawImage.add(new JButton("Foo"));
            JFrame frame = new JFrame("Draw Image");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(drawImage);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
         }
      });
   }

   public DrawImagePanelKeyBindings() {
      setBindings();
      URL imgURL;
      try {
         imgURL = new URL(IMAGE_PATH);
         image = ImageIO.read(imgURL);
         initialSize = new Dimension(image.getWidth(this),
               image.getHeight(this));
         setVisible(true);
      } catch (MalformedURLException e) {
         e.printStackTrace();
      } catch (IOException e) {
         e.printStackTrace();
      }
   }

   private void setBindings() {
      int condition = WHEN_IN_FOCUSED_WINDOW;
      InputMap inputMap = getInputMap(condition);
      ActionMap actionMap = getActionMap();

      final KeyStroke[] keyStrokes = {
            KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0),
            KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0)
      };
      for (final KeyStroke keyStroke : keyStrokes) {
         inputMap.put(keyStroke, keyStroke.toString());
         actionMap.put(keyStroke.toString(), new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent evt) {
               myKeyPressed(keyStroke.getKeyCode());
            }
         });
      }
   }

   @Override
   public Dimension getPreferredSize() {
      return initialSize;
   }

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      g.setColor(Color.BLACK);
      g.fillRect(0, 0, getSize().width, getSize().height);
      Graphics2D g2d = (Graphics2D) g.create();
      g2d.scale(scale, scale);
      g2d.drawImage(image, 0, 0, this);
   }

   public void myKeyPressed(int key) {
      if (key == KeyEvent.VK_UP) {
         scale *= MULTIPLIER;
      }
      if (key == KeyEvent.VK_DOWN) {
         scale /= MULTIPLIER;
      }
      repaint();
   }
}
查看更多
登录 后发表回答