在类中使用addKeyListener来侦听来自另一个类的键?

时间:2017-04-17 02:02:55

标签: java swing keylistener

我正在尝试使用线程来监听密钥 - 这是创建JPanel的不同类。以下是JPanel所在的类。

public class Game extends JPanel {

public static final int WIDTH = 600,
                        HEIGHT = 650;

private static boolean running;

private BufferedImage image;
private Graphics2D g;   

public String str;
private static Thread MyThread;


public Game() {
    super();
    setPreferredSize(new Dimension(WIDTH, HEIGHT));
    setFocusable(true);
    requestFocus();
}



public void addNotify(){
    super.addNotify();
    MyThread myThread = new MyThread(this);

    if(myThread == null){
        myThread = new Thread(myThread);
        myThread.start();
    }
}


public void run() {
    image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
    g = (Graphics2D) image.getGraphics();

    while(running){
        update();
        render();
        draw();
        System.out.println(str);                                    
    }
    stop();
}
private void update(){  }

private synchronized void render(){
    g.setColor(Color.BLACK);
    g.fillRect(0, 0, WIDTH, HEIGHT);

}

private synchronized void draw(){
    Graphics g2 = this.getGraphics();
    g2.drawImage(image, 0, 0, null);
    g2.dispose();
}
public void start(){
    if(running) return;
    running = true;
    run();
}

private void stop() {
    if(!running) return;
    running = false;

    try{
        myThread.join();
    } catch (InterruptedException e){
        e.printStackTrace();
    }
}

}

以下是侦听Game类的线程的代码:

public class MyThread  implements Runnable, KeyListener{

private static Game game;
private static boolean left, right;

public MyThread(Game game) {
    this.game = game;       
}

@Override
public void run() {
    game.addKeyListener(this);
    if(left){game.str = "left";}
    if(right){game.str = "right";}
}


@Override
public void keyPressed(KeyEvent e) {
    int key = e.getKeyCode();
    if(key == KeyEvent.VK_LEFT){left = true;}
    if(key == KeyEvent.VK_RIGHT){right = true;}
}

@Override
public void keyReleased(KeyEvent e) {
    int key = e.getKeyCode();
    if(key == KeyEvent.VK_LEFT){left = false;}
    if(key == KeyEvent.VK_RIGHT){right = false;}
}

@Override
public void keyTyped(KeyEvent e) {

}
}

目前,当我运行代码时,即使我尝试单击左右箭头键,它也只打印出null。我尝试在game.str = "string";方法中插入run(),控制台打印出string,因此该线程有效。我认为问题是MyThread没有收听JPanel。你能告诉我如何解决这个问题 - 有一个单独的线程来听取Game课程吗?

PS。 game.addKeyListener(this)位于run()类的MyThread方法中。

1 个答案:

答案 0 :(得分:1)

您需要一些方法来模拟用户输入,就个人而言,我从一个简单的enum开始......

public enum Input {
    LEFT, RIGHT;
}

接下来,我们需要某种方式通知感兴趣的各方发生了某种输入事件......

public interface InputHandler {
    public void add(Input input);
    public void remove(Input input);
}

接下来,你的关键监听器需要一些方法将事件传回观察者......

免责声明:我不推荐KeyListener的用户,有关详细信息,请参阅结尾

public class KeyHandler extends KeyAdapter {

    private InputHandler inputHandler;

    public KeyHandler(InputHandler handler) {
        this.inputHandler = handler;
    }

    @Override
    public void keyPressed(KeyEvent e) {
        int key = e.getKeyCode();
        if (key == KeyEvent.VK_LEFT) {
            inputHandler.add(Input.LEFT);
        } else if (key == KeyEvent.VK_RIGHT) {
            inputHandler.add(Input.RIGHT);
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {
        int key = e.getKeyCode();
        if (key == KeyEvent.VK_LEFT) {
            inputHandler.remove(Input.LEFT);
        } else if (key == KeyEvent.VK_RIGHT) {
            inputHandler.remove(Input.RIGHT);
        }
    }
}

现在,您的Game需要某种方式来管理输入事件......

public class Game extends JPanel implements InputHandler {

    private Set<Input> inputSet = new HashSet<>();
    //...
    public Game() {
        //...
        addKeyListener(new KeyHandler(this));
        //...
    }

    @Override
    public synchronized void add(Input input) {
        inputSet.add(input);
    }

    @Override
    public synchronized void remove(Input input) {
        inputSet.remove(input);
    }

所有这些意味着当发生某些输入事件时,会通知您Game,它可以根据事件更新当前状态模型。 Game并不关心您使用了KeyListenerJButton或其他一些输入法,将其解耦并使其更加灵活。

好的,你说,但我该如何检查哪个被按下了?

inputSet.contains(Input.LEFT)
按下true

将返回LEFT,释放时会返回false

观察

基于我可以从您的代码中推断出的内容,这...

public void run() {
    image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
    g = (Graphics2D) image.getGraphics();

    while(running){
        update();
        render();
        draw();
        System.out.println(str);                                    
    }
    stop();
}

永远不会被召唤。您正在使用Thread执行KeyListener,其运行方法只是将自己添加到面板和退出。

当你正在使用Swing时,我强烈建议使用Swing Timer,它可以降低绘制线程和更新线程之间竞争条件的风险,因为Timer是在EDT的上下文中触发,使得从

内更新UI的状态更安全

建议

现在,尽管如此,我高度建议你使用KeyListener,因为它遇到与焦点相关的问题,很难维护,提供自定义并注入其他输入方法。

相反,我建议使用Key Bindings API代替,这将解决这些短暂的提交,并提供更强大和可重复使用的解决方案

上述解决方案仍然适用于键绑定