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

Java CardLayout下如何让jPanel3获焦以激活KeyListener

问题:KeyListener按键无响应,焦点获取失败

在使用KeyListener实现屏幕元素交互(仅通过打印文本验证功能)时,出现按键无响应的问题。已确认Board类与Player类的KeyListener逻辑无误,排查后定位问题出在采用CardLayout布局的主Test类中,推测承载游戏内容的jPanel3未获得焦点。尝试过部分代码调整但未解决,现咨询如何让jPanel3及其内部组件获取焦点。


主类Test代码

import javax.swing.JFrame;
import java.awt.*; 
import javax.swing.*; 
import java.awt.event.*; 

public class Test extends JFrame implements ActionListener {
    CardLayout crd;
    JButton btn1, btn2, btn3, btn4;
    Container cPane;
    JPanel cPanel = new JPanel();
    JPanel jPanel1,jPanel2,jPanel3;
    Board board = new Board();
    private int currCard = 1;
    GridBagConstraints c = new GridBagConstraints();

    Test() {
        jPanel1 = new JPanel();
        jPanel2 = new JPanel();
        jPanel3 = new JPanel();

        btn1 = new JButton("Start");
        btn2 = new JButton("Help");
        btn3 = new JButton("Back");
        btn4 = new JButton("Exit");

        cPane = getContentPane();
        crd = new CardLayout();
        cPanel.setLayout(crd);

        buttons();
        panel1();
        panel2();
        panel3();

        cPanel.add(jPanel1, "1");
        cPanel.add(jPanel2, "2");
        cPanel.add(jPanel3, "3");

        btn1.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                currCard = 3;
                crd.show(cPanel, "" + (currCard));
            }
        });

        btn2.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                currCard = 2;
                crd.show(cPanel, "" + (currCard));
            }
        });

        btn3.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                currCard = 1;
                crd.show(cPanel, ""+ (currCard));
            }
        });

        btn4.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                System.exit(0);
            }
        });

        getContentPane().add(cPanel);
    }

    void panel1() {
        jPanel1.setBackground(Color.black);
        jPanel1.setLayout(new GridBagLayout());
        // Set the layout to GridBagLayout
        c.fill = GridBagConstraints.CENTER;
        c.insets = new Insets(15, 0, 0, 0); // Add vertical padding
        jPanel1.add(btn1, c);
        c.gridy = 1;
        jPanel1.add(btn2, c);
        c.gridy = 2;
        jPanel1.add(btn4, c);
    }

    void panel2() {
        JTextArea textField = new JTextArea("Text text");
        textField.setEditable(false);
        textField.setHighlighter(null);
        Font font = new Font("Courier", Font.BOLD, 35);
        textField.setFont(font);      
        textField.setPreferredSize(new Dimension(500, 940));
        textField.setForeground(Color.WHITE);
        textField.setBackground(Color.black);

        jPanel2.setBackground(Color.black);
        jPanel2.setLayout(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.anchor = GridBagConstraints.NORTHWEST; // Align component to the upper left corner
        gbc.insets = new Insets(15, 0, 0, 0); // Add vertical padding
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.weightx = 1.0; // Expand horizontally
        gbc.weighty = 0.5; // Expand vertically
        jPanel2.add(textField, gbc);
        gbc.gridx = 0;
        gbc.gridy = 1; // Set the grid position for btn3
        gbc.anchor = GridBagConstraints.NORTH; // Align component to the upper left corner
        jPanel2.add(btn3, gbc);
    }

    void panel3() {
        jPanel3.setFocusable(true);
        jPanel3.setRequestFocusEnabled(true);
        jPanel3.setLayout(new BorderLayout(0, 0));
        jPanel3.add(board);
        jPanel3.grabFocus();    
    }

    void buttons() {
        // make the buttons look better and make it so they look good with the background, chane the size of them 
        btn1.setPreferredSize(new Dimension(75, 50));
        btn1.setBackground(Color.black);
        btn1.setForeground(Color.WHITE);

        btn2.setPreferredSize(new Dimension(75, 50));
        btn2.setBackground(Color.black);
        btn2.setForeground(Color.WHITE);

        btn3.setPreferredSize(new Dimension(75, 50));
        btn3.setBackground(Color.black);
        btn3.setForeground(Color.WHITE);

        btn4.setPreferredSize(new Dimension(75, 50));
        btn4.setBackground(Color.black);
        btn4.setForeground(Color.WHITE);
    }

   public static void main(String argvs[]) {
        System.out.println("aaa" + System.getProperty("user.dir")); 
        Test frame = new Test();
        frame.setTitle("Space invaders");
        frame.setSize(900, 900);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
    }

    @Override
    public void actionPerformed(ActionEvent arg0) {
        throw new UnsupportedOperationException("Unimplemented method 'actionPerformed'");
    }
}

Board类代码

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Stroke;
import java.awt.event.KeyListener;

import javax.swing.JPanel;

public class Board extends JPanel {
    Player player = new Player(50, 50, 50, 50);

    public Board(){
        addKeyListener((KeyListener) player);
        setFocusable(true);
    }

    public void paint(Graphics g) {
        g.setColor(Color.DARK_GRAY);
        g.fillRect(0, 0, 900, 900);
        g.setColor(Color.WHITE);            
        g.drawRect (20, 20, 864, 624);  
        g.setColor(Color.BLACK);
        g.fillRect (21, 21, 863, 623);
        g.setColor(Color.WHITE);    
        g.setFont(new Font("arial",Font.PLAIN,14));
        player.draw(g);
        //repaint();
    }

    public void board(Graphics g) {
        Graphics2D g2d = (Graphics2D) g;
        Stroke stroke1 = new BasicStroke(4f);
        g2d.setColor(Color.white);
        g2d.setStroke(stroke1);
        g2d.drawRect(20, 50, 850, 600);
        g2d.setColor(Color.white);
        float[] dashingPattern2 = {10f, 4f};
        Stroke stroke2 = new BasicStroke(4f, BasicStroke.CAP_BUTT,
        BasicStroke.JOIN_MITER, 1.0f, dashingPattern2, 0.0f);
        g2d.setStroke(stroke2);
        g2d.drawLine(448, 50, 448, 650);
        g.setFont(new Font("arial",Font.PLAIN,30));
    }
}

Player类代码

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JPanel;

public class Player extends JPanel implements KeyListener  {
    public int playerXpos;
    public int playerYpos;
    public int playerWidth;
    public int playerHeight;
    public int speed = 2;
    boolean down = false;
    boolean up = false;
    Rectangle player;

    public Player(int playerXpos, int playerYpos, int playerWidth, int playerHeight){
        this.playerXpos = playerXpos;
        this.playerYpos = playerYpos;
        this.playerWidth = playerWidth;
        this.playerWidth = playerHeight;
        player = new Rectangle(playerXpos, playerYpos, playerWidth, playerHeight);
    } 

    protected void draw(Graphics g) {
        playerXpos = 500;
        playerYpos = 60;
        playerWidth = 50;
        playerHeight = 30;
        g.setColor(Color.CYAN);
        g.fillRect(playerXpos, playerYpos, playerWidth, playerHeight);
    }

    @Override
    public void keyPressed(KeyEvent keyCode) {
        if (keyCode.getKeyCode() == KeyEvent.VK_UP) {
            System.out.println("Up Arrrow-Key is pressed!");
        }
        else if (keyCode.getKeyCode() == KeyEvent.VK_DOWN) {
            System.out.println("Down Arrrow-Key is pressed!");
        }
        else if (keyCode.getKeyCode() == KeyEvent.VK_LEFT) {
            System.out.println("Left Arrrow-Key is pressed!");
        }
        else if (keyCode.getKeyCode() == KeyEvent.VK_RIGHT) {
           System.out.println("Right Arrrow-Key is pressed!");
        }    
    }

    @Override
    public void keyReleased(KeyEvent arg0) {
        throw new UnsupportedOperationException("Unimplemented method 'keyReleased'");
    }

    @Override
    public void keyTyped(KeyEvent arg0) {
        throw new UnsupportedOperationException("Unimplemented method 'keyTyped'");
    }
}

解决方案

1. 切换卡片后主动请求焦点

grabFocus()在组件未显示时调用无效,必须在Card切换完成后,让实际承载KeyListener的Board组件请求焦点。修改btn1的ActionListener:

btn1.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent ae) {
        currCard = 3;
        crd.show(cPanel, "" + (currCard));
        // 卡片显示后,让Board组件请求焦点
        board.requestFocusInWindow();
    }
});

2. 优化组件焦点设置

在Board类构造方法中添加setRequestFocusEnabled(true),确保组件可以主动获取焦点:

public Board(){
    addKeyListener((KeyListener) player);
    setFocusable(true);
    setRequestFocusEnabled(true);
}

3. 替换KeyListener为KeyBindings(推荐)

KeyListener对焦点依赖极强,Swing官方更推荐使用KeyBindings处理按键事件,无需组件获得焦点也能响应。修改Board类:

public Board(){
    // 移除原KeyListener
    // addKeyListener((KeyListener) player);

    // 使用KeyBindings绑定方向键
    InputMap inputMap = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
    ActionMap actionMap = getActionMap();

    // 绑定上箭头
    inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "up");
    actionMap.put("up", new AbstractAction() {
        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("Up Arrrow-Key is pressed!");
        }
    });

    // 绑定下箭头
    inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "down");
    actionMap.put("down", new AbstractAction() {
        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("Down Arrrow-Key is pressed!");
        }
    });

    // 同理绑定左右箭头...
    setFocusable(true);
}

4. 修复Player类笔误

Player构造方法中this.playerWidth = playerHeight;是错误,应改为this.playerHeight = playerHeight;,否则会导致绘制的玩家矩形尺寸异常。


内容的提问来源于stack exchange,提问作者Jon doe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 07:50:54