You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在actionPerformed方法中检测键盘输入或传递键盘输入结果至该方法?

解决方法

核心思路是通过键盘监听器记录输入状态,在Timer触发的actionPerformed方法中读取这些状态来控制逻辑,而不是直接在actionPerformed内检测键盘输入。下面是两种可行的实现方案:

方案一:使用KeyBindings(推荐,避免焦点问题)

KeyBindings比KeyListener更可靠,它不依赖组件获取焦点,适合游戏这类需要全局响应键盘输入的场景。我们可以给myJPanel绑定方向键,通过按键事件修改速度变量,再在actionPerformed中更新位置:

修改后的myJPanel类代码:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class myJPanel extends JPanel implements ActionListener {
    final int PANEL_WIDTH = 500;
    final int PANEL_HEIGHT = 500;
    final int SNAKE_WIDTH = 25;
    final int SNAKE_HEIGHT = 25;
    Timer timer;
    int xVelocity = 0;
    int yVelocity = 0;
    int xPosition = 250;
    int yPosition = 250;

    myJPanel(){
        this.setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));
        this.setBackground(Color.BLACK);
        // 绑定方向键
        setupKeyBindings();
        
        timer = new Timer(100, this); // 调整间隔让移动更平滑
        timer.start();
    }

    private void setupKeyBindings() {
        // 向上键
        this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("UP"), "moveUp");
        this.getActionMap().put("moveUp", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                xVelocity = 0;
                yVelocity = -5; // 控制移动步长
            }
        });

        // 向下键
        this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("DOWN"), "moveDown");
        this.getActionMap().put("moveDown", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                xVelocity = 0;
                yVelocity = 5;
            }
        });

        // 向左键
        this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("LEFT"), "moveLeft");
        this.getActionMap().put("moveLeft", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                xVelocity = -5;
                yVelocity = 0;
            }
        });

        // 向右键
        this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("RIGHT"), "moveRight");
        this.getActionMap().put("moveRight", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                xVelocity = 5;
                yVelocity = 0;
            }
        });
    }

    @Override
    protected void paintComponent(Graphics g) { // Swing推荐重写paintComponent而非paint
        super.paintComponent(g);
        Graphics2D g2D = (Graphics2D) g;
        g2D.setColor(Color.GREEN);
        g2D.fillRect(xPosition, yPosition, SNAKE_WIDTH, SNAKE_HEIGHT);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        // 更新蛇的位置
        xPosition += xVelocity;
        yPosition += yVelocity;
        // 边界检测(防止蛇移出面板)
        xPosition = Math.max(0, Math.min(PANEL_WIDTH - SNAKE_WIDTH, xPosition));
        yPosition = Math.max(0, Math.min(PANEL_HEIGHT - SNAKE_HEIGHT, yPosition));
        repaint();
    }
}

方案二:使用KeyListener(需注意焦点)

如果选择KeyListener,需要确保myJPanel能获取焦点,同时重写keyPressed方法记录键盘输入:

修改后的myJPanel类代码:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

public class myJPanel extends JPanel implements ActionListener, KeyListener {
    final int PANEL_WIDTH = 500;
    final int PANEL_HEIGHT = 500;
    final int SNAKE_WIDTH = 25;
    final int SNAKE_HEIGHT = 25;
    Timer timer;
    int xVelocity = 0;
    int yVelocity = 0;
    int xPosition = 250;
    int yPosition = 250;

    myJPanel(){
        this.setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));
        this.setBackground(Color.BLACK);
        this.setFocusable(true); // 必须设置为可获取焦点
        this.addKeyListener(this);
        
        timer = new Timer(100, this);
        timer.start();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2D = (Graphics2D) g;
        g2D.setColor(Color.GREEN);
        g2D.fillRect(xPosition, yPosition, SNAKE_WIDTH, SNAKE_HEIGHT);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        xPosition += xVelocity;
        yPosition += yVelocity;
        // 边界检测
        xPosition = Math.max(0, Math.min(PANEL_WIDTH - SNAKE_WIDTH, xPosition));
        yPosition = Math.max(0, Math.min(PANEL_HEIGHT - SNAKE_HEIGHT, yPosition));
        repaint();
    }

    @Override
    public void keyPressed(KeyEvent e) {
        int key = e.getKeyCode();
        switch (key) {
            case KeyEvent.VK_UP:
                xVelocity = 0;
                yVelocity = -5;
                break;
            case KeyEvent.VK_DOWN:
                xVelocity = 0;
                yVelocity = 5;
                break;
            case KeyEvent.VK_LEFT:
                xVelocity = -5;
                yVelocity = 0;
                break;
            case KeyEvent.VK_RIGHT:
                xVelocity = 5;
                yVelocity = 0;
                break;
        }
    }

    @Override
    public void keyTyped(KeyEvent e) {}

    @Override
    public void keyReleased(KeyEvent e) {}
}

关键注意事项

  1. 不要在actionPerformed内直接检测键盘输入:Timer的定时事件和键盘输入事件属于不同的事件队列,直接检测会导致逻辑冲突。
  2. 用paintComponent替代paint:这是Swing自定义绘制的最佳实践,能正确处理组件背景和边界渲染。
  3. 调整移动参数:通过修改Timer的间隔(如100毫秒)和速度变量(如±5),可以控制蛇的移动平滑度和速度。

内容的提问来源于stack exchange,提问作者Tim Asher

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 11:22:56