首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >KeyListener方法keyPressed不工作

KeyListener方法keyPressed不工作
EN

Stack Overflow用户
提问于 2020-04-25 13:18:39
回答 2查看 48关注 0票数 0

我正在尝试用java swing做一个小游戏,并且已经取得了相当大的进展,但是现在我的KeyListener没有使用keyPressed方法。

下面是我的一些代码

代码语言:javascript
复制
public class Screen extends JPanel implements Runnable{

private static final int WIDTH = 300, HEIGHT = 300, RIGHT = 0, LEFT = 1, UP = 2, DOWN = 3, STILL = 4;
private Thread thread;
private boolean running = false;
private int direction = DOWN;

   public Screen() {
    setFocusable(true);
    addKeyListener(new Key());
    setPreferredSize(new Dimension(WIDTH, HEIGHT));
    start();
   }

   public void tick(){
     System.out.println(direction)  
   }

   public void start() {
    running = true;
    thread = new Thread(this, "Game Loop");
    thread.start();
}

   public void run() {
     while (running) {
       tick();
       repaint();
    }
}

   private class Key implements KeyListener{
    @Override
    public void keyTyped(KeyEvent e) {
        // TODO Auto-generated method stub

    }

    @Override
    public void keyPressed(KeyEvent e) {
        int key = e.getKeyCode();

        if (key == KeyEvent.VK_D) {
            direction = RIGHT;
        }

        if (key == KeyEvent.VK_A) {
            direction = LEFT;
        }

        if (key == KeyEvent.VK_W) {
            direction = UP;
        }

        if (key == KeyEvent.VK_S) {
            direction = DOWN;
        }

    }

    @Override
    public void keyReleased(KeyEvent e) {
        // TODO Auto-generated method stub

    }
   }
}

我正在查看控制台,并期望输出从3(按下)变为我按下的任何按钮,但它永远不会改变。过了一段时间后,我意识到keyPressed方法有问题,我只是不知道是什么问题。

EN

回答 2

Stack Overflow用户

发布于 2020-04-25 15:04:02

你不需要单独的“游戏循环”线程。Swing是事件驱动的。当用户按下计算机键盘上的某个键时,将调用您的按键侦听器代码。

正如MadProgrammer在他的comment to your question中提到的

在EDT上下文之外修改UI的状态可能会导致绘画问题

换句话说,Swing是single threaded的,这个线程被称为事件分派线程( Event Dispatch thread,EDT ),所有更改图形用户界面的代码都应该只在事件分派线程上执行。

下面的代码是一个独立的Swing应用程序,它显示一个可聚焦的JPanel。当您按下键盘上的一个相关“方向”键时,即A或D或S或W,控制台将显示方向。按下任何其他键时,方向仍会写入控制台。请注意,我对各个方向使用了enum,而不是整数常量。

代码语言:javascript
复制
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;

public class Screen01 extends JPanel implements Runnable {
    public enum Directions {RIGHT, LEFT, UP, DOWN, STILL};

    private static final int WIDTH = 300, HEIGHT = 300;
    private Directions direction;

    public Screen01() {
        setPreferredSize(new Dimension(WIDTH, HEIGHT));
        setFocusable(true);
        addKeyListener(new Key());
    }

    public void run() {
        createAndDisplayGui();
    }

    private void createAndDisplayGui() {
        JFrame frame = new JFrame("Screen");
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.add(this, BorderLayout.CENTER);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    private class Key implements KeyListener {

        @Override
        public void keyTyped(KeyEvent e) {
            // Do nothing.
        }

        @Override
        public void keyPressed(KeyEvent e) {
            int key = e.getKeyCode();
            switch (key) {
                case KeyEvent.VK_D:
                    direction = Directions.RIGHT;
                    break;
                case KeyEvent.VK_A:
                    direction = Directions.LEFT;
                    break;
                case KeyEvent.VK_W:
                    direction = Directions.UP;
                    break;
                case KeyEvent.VK_S:
                    direction = Directions.DOWN;
                    break;
                default:
                    direction = Directions.STILL;
            }
            System.out.println("Direction = " + direction);
        }

        @Override
        public void keyReleased(KeyEvent e) {
            // Do nothing.
        }
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Screen01());
    }
}
票数 0
EN

Stack Overflow用户

发布于 2020-04-26 07:51:19

利用key bindings API,它将解决这个和所有其他与KeyListener相关的问题,而不会有太多的黑客攻击,也不会弄清楚为什么它“有时”会工作

Swing是单线程的。这意味着您在从事件分派线程的上下文之外(直接或间接)更新UI的状态时应格外小心,这也包括UI可能依赖的任何状态。

有关更多详细信息,请查看Concurrency in Swing

在没有更多细节的情况下,最简单的解决方案之一是使用Swing Timer作为主要的“勾选”操作。每当它“滴答作响”时,您将检查输入的状态,更新模型的状态,并触发重新绘制来更新UI

例如..。

代码语言:javascript
复制
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.HashSet;
import java.util.Set;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;

public class Main {

    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public static class TestPane extends JPanel {

        enum Input {
            UP, DOWN, LEFT, RIGHT
        }

        private Set<Input> input = new HashSet<>();

        public TestPane() {
            InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
            ActionMap am = getActionMap();

            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, false), "Left.pressed");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, true), "Left.released");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, false), "Right.pressed");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, true), "Right.released");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, false), "Up.pressed");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, true), "Up.released");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, false), "Down.pressed");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, true), "Down.released");

            am.put("Left.pressed", new InputAction(Input.LEFT, input, false));
            am.put("Left.released", new InputAction(Input.LEFT, input, true));
            am.put("Right.pressed", new InputAction(Input.RIGHT, input, false));
            am.put("Right.released", new InputAction(Input.RIGHT, input, true));
            am.put("Down.pressed", new InputAction(Input.DOWN, input, false));
            am.put("Down.released", new InputAction(Input.DOWN, input, true));
            am.put("Up.pressed", new InputAction(Input.UP, input, false));
            am.put("Up.released", new InputAction(Input.UP, input, true));

            input.add(Input.UP);
            input.add(Input.DOWN);
            input.add(Input.LEFT);
            input.add(Input.RIGHT);

            Timer timer = new Timer(5, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    // Check what's currently been pressed
                    // and update the state accordingly...
                    if (input.contains(Input.UP)) {
                        //..
                    }
                    if (input.contains(Input.DOWN)) {
                        //..
                    }
                    if (input.contains(Input.LEFT)) {
                        //..
                    }
                    if (input.contains(Input.RIGHT)) {
                        //..
                    }
                    repaint();
                }
            });
            timer.start();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();

            int midX = getWidth() / 2;
            int midY = getHeight() / 2;

            FontMetrics fm = g2d.getFontMetrics();
            int spaceWidth = fm.stringWidth("M");
            int spaceHeight = fm.getHeight();

            if (input.contains(Input.UP)) {
                String text = "UP";
                g2d.drawString(text,
                        midX - (fm.stringWidth(text)) / 2,
                        (midY - (spaceHeight * 2) + fm.getAscent()));
            }
            if (input.contains(Input.DOWN)) {
                String text = "DOWN";
                g2d.drawString(text,
                        midX - (fm.stringWidth(text)) / 2,
                        (midY + spaceHeight + fm.getAscent()));
            }
            if (input.contains(Input.LEFT)) {
                String text = "LEFT";
                g2d.drawString(text,
                        (midX - spaceWidth - fm.stringWidth(text)),
                        (midY + (fm.getAscent() / 2)));
            }
            if (input.contains(Input.RIGHT)) {
                String text = "RIGHT";
                g2d.drawString(text,
                        (midX + spaceWidth),
                        (midY + (fm.getAscent() / 2)));
            }
            g2d.dispose();
        }

        public class InputAction extends AbstractAction {

            private Input input;
            private Set<Input> inputSet;
            private boolean onRelease;

            public InputAction(Input input, Set<Input> inputSet, boolean onRelease) {
                this.input = input;
                this.inputSet = inputSet;
                this.onRelease = onRelease;
            }

            @Override
            public void actionPerformed(ActionEvent e) {
                if (onRelease) {
                    inputSet.remove(input);
                } else {
                    inputSet.add(input);
                }
            }

        }

    }
}

这是一个基本的示例,它直接在paintComponent方法中打印按键输入的状态。更实际的实现是使用TimerActionListener来检查input状态,并相应地更改所需的模型,如示例中所暗示的那样。

如果您需要更直接的控制,那么您需要避免使用Swing绘画系统,而是自己进行控制。为此,您需要使用BufferStrategy。这是一个更复杂的解决方案,但它也更强大,因为您可以完全控制何时更新UI

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61421299

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档