2017-04-17 9 views
0

JPanelが作成された場所とは異なるクラスであるキーを聴くスレッドを作成しようとしています。以下は、JPanelのクラスです。以下はクラス内のaddKeyListenerを使用して、別のクラスのキーをリッスンしますか?

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を出力をし、右矢印キー。 run()メソッドにgame.str = "string";を挿入しようとしましたが、コンソールはstringを出力してスレッドが機能します。問題は、MyThreadJPanelを聞いていないということです。 Gameクラスを聞く別のスレッドを持つことで、どうすればこの問題を解決できますか教えてください。

PS。 game.addKeyListener(this)は、MyThreadクラスのrun()メソッドにあります。

+0

私は非常だろう'KeyListener'を介してKey Bindings APIを使うことをお勧めします。これは' KeyListener' APIに関連したいくつかの問題を解決します - [Key Bindingsの使い方]を参照してください(http://docs.oracle.com/javase/tutorial /uiswing/misc/keybinding.html) – MadProgrammer

+0

あなたの 'KeyListener'は' Game'クラスに関連付けられた状態を更新するのではなく、それ自身を更新する必要があります。 – MadProgrammer

+0

可能な複製[キーリスナーの代わりにキーバインディングを使用する方法](http://stackoverflow.com/questions/22741215/how-to-use-key-bindings-instead-of-key-listeners) – user1803551

答えて

1

あなたが個人的に、私はシンプルenum ...

public enum Input { 
    LEFT, RIGHT; 
} 

次で始まるだろう、我々はその入力イベントのいくつかの種類の利害関係者に通知する方法が必要、ユーザー入力をモデル化するためにいくつかの方法が必要です...

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

次に、あなたのキーリスナーが戻って観察者にイベントを伝達するためにいくつかの方法を必要と...

ディスクを発生していますライマー:私は詳細

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); 
     } 
    } 
} 

の終了を参照してください、KeyListenerのユーザーにはお勧めしませんが、あなたの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は、KeyListener、またはJButtonなどの入力方法を使用していることに気付かず、それをデカップリングしてより柔軟にします。

さて、あなたは言うが、どれが押されているかを確認するにはどうすればよいですか?それが解放されたときに

inputSet.contains(Input.LEFT) 

LEFTが押された truefalseを返します。私は、あなたのコードからこれを推測することができるものに基づいて

観察

を...

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(); 
} 

が呼び出されることはありません。 KeyListenerを使用してThreadを実行しています。その実行方法は、パネルと出口に自分自身を追加するだけです。

あなたはスイングを使用しているように、私は強く代わりにスイングTimerを使用することをお勧めしますTimerはEDTのコンテキストでトリガーされるように、それが作り、絵のスレッドと更新スレッド間の競合条件のリスクを低減それはより安全な、今

勧告内

からUIの状態を更新するためにすべてのことを言った、私は非常には、それがフォーカス関連の問題に苦しんでいるとして、あなたがは、ない使用KeyListenerを行うされてお勧めします維持するのが難しい、欲しいものを提供する他の入力方法を注入する必要があります。

代わりに、私はこれらの短い来を解決し、より堅牢で再利用可能なソリューション

にまだキーバインディングで動作します上記のソリューションを提供しますこれは、代わりにKey Bindings APIを使用してお勧めしたい

+0

これは機能しますが、 KeyHandlerスレッド? –

+0

なぜあなたは気になりますか?キーイベントは、EDTの文脈で依然として派遣される予定です。代わりに、あなたのオブザーバー/更新ループは、独自のスレッドで実行する必要があります – MadProgrammer

関連する問題