2017-04-17 1 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을 출력하므로 스레드가 작동합니다. 문제는 MyThread이 (가) JPanel입니다. 이 문제를 해결하려면 어떻게해야합니까? Game 클래스를 듣기 위해 별도의 스레드가 있어야합니까?

추신. 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

가능한 중복 [키 리스너 대신 키 바인딩을 사용하는 방법] (0120-388-333) – user1803551

답변

1

당신은

public enum Input { 
    LEFT, RIGHT; 
} 

다음으로, 우리는 이해 관계자에게 알릴 수있는 방법이 필요합니다 ... 개인적으로, 나는 간단한 enum로 시작하는 것, 사용자 입력을 모델링하기 위해 어떤 방법을 필요로하는 입력 이벤트의 일종 발생 ...

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

다음으로, 키 리스너는 ...

디스크를 다시 관찰자에 이벤트를 전달하는 몇 가지 방법이 필요 laimer : 나는 자세한 내용은 지금

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에 알리고 이벤트를 기반으로 현재 상태 모델을 업데이트 할 수 있습니다. GameKeyListener 또는 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이 EDT의 컨텍스트에서 트리거되기 때문에 스윙 Timer을 사용하는 것이 좋습니다. 페인팅 스레드와 업데이트 스레드 간의 경쟁 조건의 위험을 줄입니다. 좀 더 안전하게, 지금

권장 내

에서 UI의 상태를 업데이트하기 위해 모두가 그렇게 말한다면, 나는 매우, 그것은 초점 관련 문제 앓고 당신이 은하지 사용 KeyListener을한다 추천 유지하기 어렵고, 고객에게 제공한다. 오해를 피하고 다른 입력 방법을 주입 할 수 있습니다.

대신에, 나는이 짧은오고를 해결하고 더 강력하고 재사용 가능한 솔루션

에게 여전히 키 바인딩과 함께 작동합니다 위의 솔루션을 제공하는 대신 Key Bindings API을 사용하는 것이 좋습니다 것

+0

이 작업은 가능하지만 스레드를 KeyHandler? –

+0

왜 당신이 신경 쓸까요? 키 이벤트는 여전히 EDT의 맥락에서 파견 될 것입니다. 대신 관찰자/업데이트 루프가 자체 스레드에서 실행되어야합니다. – MadProgrammer

관련 문제