¿Por qué mi programa Java no reconoce ninguna pulsación de tecla usando KeyListener?

Literalmente no tengo idea de por qué mi programa no reconoce la entrada del teclado. He colocado instrucciones de impresión en todo el programa para determinar el problema y he determinado que el método keyPressed nunca se activa. Esto es para un juego que estoy haciendo para un proyecto de clase, y sí, soy un programador relativamente principiante. ¡Gracias por adelantado! (Código abajo)

import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.ImageIcon;
import javax.swing.JApplet;


public class Dodger extends JApplet implements Runnable, KeyListener {

Thread myThread;

public Image bg;
public Image pic;
public boolean loaded;

public int cx, cy, speed, x, y;

public void init(){
    setSize(800,800);
    loaded = false;
    x = 2;
    y = 400;
    cx = 0;
    cy = 0;
    speed = 3;
    myThread = new Thread(this);
    myThread.start();
    addKeyListener(this);
}

public void run(){
    loadpic();
    repaint();
    while (myThread!=null)
    {

        try{
            myThread.sleep(18);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        repaint();
    }
}

public void upMotion(){
    cy = cy + speed;
}

public void downMotion(){
    cy = cy - speed;    
}

public void leftMotion(){
    cx = cx - speed;
}

public void rightMotion(){
    cx = cx + speed;
}

@Override
public void keyPressed(KeyEvent k) {

    if (k.getKeyCode() == KeyEvent.VK_LEFT) {
        System.out.println("work");
        leftMotion();
    }

    if (k.getKeyCode() == KeyEvent.VK_RIGHT) {
        rightMotion();
    }

    if (k.getKeyCode() == KeyEvent.VK_UP) {
        upMotion();
    }

    if (k.getKeyCode() == KeyEvent.VK_DOWN) {
        downMotion();
    }
}

@Override
public void keyReleased(KeyEvent e) {
}

@Override
public void keyTyped(KeyEvent e) {
}

public void loadpic(){
    bg = new ImageIcon(getClass().getResource("back.png")).getImage();
    pic = new ImageIcon(getClass().getResource("smile.png")).getImage();
    loaded = true;
    repaint();
}



public void paint(Graphics g){
    g.drawImage(bg, 0, 0, this);
    g.drawImage(pic, cx, cy, this);
}   
}

preguntado el 12 de junio de 12 a las 15:06

¿Te has asegurado de que el applet tenga foco? Sin el applet que tiene pulsaciones de tecla de enfoque, no se capturará. -

Para obtener una mejor ayuda antes, publique un SSCCE. Tenga en cuenta que tuve que pasar algunos momentos solucionando la falta de imágenes, antes de poder ver el applet y las teorías de prueba. -

3 Respuestas

Los eventos clave se detectan correctamente cuando el subprograma es enfocable y tiene foco. Administrar el enfoque siempre ha sido un problema con los applets. El problema es principalmente que Sun nunca se molestó en especificar el comportamiento de enfoque que idealmente debería aplicarse a una página mixta de elementos HTML enfocables y subprogramas.

Según el consejo de Tom, agregue al final de init()

setFocusable(true);

Para estar seguro, anula también:

public void start() {
    this.requestFocusInWindow();
}

Por otro lado, generalmente es mejor usar combinaciones de teclas en columpio. También requieren que el subprograma tenga foco de entrada.


Respondido el 12 de junio de 12 a las 16:06

@mKorbel Cada vez que veo Swing & KeyListener juntos, pienso en combinaciones de teclas... - Andrew Thompson

En primer lugar, probablemente desee separar su clase de su KeyListener, ya que hace que sea un poco más difícil de leer.

A continuación, desea deshacerse de la desnuda Thread y envuélvalo en un temporizador.

import javax.swing.Timer;

class Dodger extends JApplet {

    Timer imageUpdater; //replaces Thread

    /*...*/
    public void init() {
        /*etc*/
        loadpic();
        int repaintInterval = 100;
        imageUpdater = new Timer(repaintInterval,
            new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    repaint();
                }
            }
        );
        imageUpdater.start();
        addKeyListener(new KeyHandler());
        setFocusable(true);
     }

    /*...*/
    private class KeyHandler extends KeyAdapter {
        /* Note that with this implementation, you do not have to override
         * unnecessary methods, as KeyAdapter is an abstract class that
         * implements all of the methods of KeyListener.
         */
        @Override
        public void keyPressed(KeyEvent e) {
        /*...*/
        }
    }
    /*...*/
}

La mayor parte de esto es solo limpieza de código: el problema real puede solucionarse (según Tom, vea los comentarios) con el setFocusable(true).

Respondido el 12 de junio de 12 a las 16:06

Cuando se trata del problema en sí, setFocusable(true) en init() debería solucionarlo. - toniedzwiedz

@Tom Curiosamente (para mí), el subprograma ya se puede enfocar cuando se inicia con Eclipse. - Andrew Thompson

@AndrewThompson para mí, no es enfocable. Lanzado usando Netbeans 7.1.1 y Oracle JDK 1.7. Para ser honesto, siempre he encontrado que los applets de enfoque son un poco confusos. Una llamada adicional de setFocusable no puede hacer ningún daño de todos modos. - toniedzwiedz

@ Tom "Una llamada adicional de setFocusable no puede hacer ningún daño de todos modos". Estás bien. Lo acabo de probar y solucionó el problema de 'sin enfoque inmediato' cuando se ejecuta desde Eclipse. "Siempre he encontrado que los applets de enfoque son un poco confusos". Cuéntame sobre eso. Vea la introducción a mi respuesta (muy editada). - Andrew Thompson

No estoy muy seguro de si se aplica a los Applets, pero su Thread puede no permitir que ocurra el envío del evento.

Trate de ejecutar su "trabajo" con SwingWorker.

Respondido el 12 de junio de 12 a las 16:06

+1 para SwingWorkker, pero tal vez no, Thread está envuelto en Runnable, el problema podría ser divertido dormir a 18 milisegundos - mKorbel

@mKorbel No creo SwingWorker es útil aquí. Esta no es una 'tarea de larga duración' (dibujar 2 imágenes en un área de visualización de 800x800), es solo una 'tarea repetitiva', solo necesita un Timer.. - Andrew Thompson

De todos modos, creo que cualquier subproceso que modifique la GUI debe ejecutarse en el subproceso de envío de eventos de Swing, como se indica en el documento de SwingWorkers. No hará ningún daño... - PedroMmm

@PeterMmm después de todas las discusiones (con Andrew Thompson) podemos hablar de - mKorbel

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.