【问题标题】:Implementing Keyboard Listener with a Java Applet使用 Java Applet 实现键盘侦听器
【发布时间】:2016-02-28 22:03:38
【问题描述】:

我正在尝试用 Java 创建一个简单的游戏。我正在使用 BlueJ IDE,我的代码目前如下:

import java.util.*;
import java.awt.*;
import javax.swing.*;    

public class GameGraphic extends JApplet
{

    // Variable initialization
    private Board board;
    private Dice dice;
    private ArrayList<Player> players; 
    private Player currentPlayer;

    // etc..

    public void init()
    {
        setSize(600,800);

        // Code to initialize game, load images
        // etc..

    }

    // Game method etc..

    public void paint(Graphics g)
    {
        // Drawing game board etc..

        turn++;
        int diceRoll = dice.roll();


        advancePlayer(currentPlayer, steps);
        changeCoins(currentPlayer, diceRoll);

        whoseTurn = (whoseTurn+1)%players.size();

        while(command=="w") {
        }

        try {
        Thread.sleep(3000);
        } catch(InterruptedException ex) {
        Thread.currentThread().interrupt();
        } 

        revalidate();
        repaint();
    }
}

所以现在,它被用于模拟,一切正常,每 3 秒进入下一个回合。我想要做的是使用键盘输入进入下一个回合。我希望它基本上绘制板,等到输入一个字符,如果字符是“n”然后前进一圈(基本上运行paint()进行一次迭代并再次等待)。 实现这一点的最佳方法是什么?我尝试使用 KeyListener,但它看起来不适用于 AWT。非常感谢:)

【问题讨论】:

标签: java swing keylistener japplet


【解决方案1】:

让我们开始吧,applet 正式来说是一种死技术,我不会浪费时间尝试让它们工作,相反,我会将您的精力集中在 API 的其他领域。请参阅Java Plugin support deprecatedMoving to a Plugin-Free Web 了解更多详情。

您永远不应该在事件调度线程的上下文中调用Thread.sleep(或执行任何其他长时间运行或阻塞操作),尤其是不要从绘制方法的上下文中调用。详情请见Concurrency in Java

你不应该调用任何可以直接或间接产生重绘的方法,绘画是为了绘画而不是别的,这样做可能会使 EDT 饿死并导致你的程序变得无响应。

在 Swing 中制作动画的一个简单解决方案是使用 Swing Timer,它不会阻止 EDT,但会触发它在 EDT 的内容中更新,从而可以安全地从内部更新 UI。

更多详情请见How to use Swing Timers

我还建议您查看Painting in AWT and SwingPerforming Custom Painting,因为如果您打算进行任何类型的自定义绘画,您应该对绘画过程的工作原理有所了解。

KeyListener 是一个低级 API,存在键盘焦点问题(如果它注册的组件没有键盘焦点,则不会生成事件),您应该使用 Key Bindings API而是。

在以下示例中,发生了两件事。这是一个Timer,它正在更新“运行时间”值,当您按下 N 键时,它会更新一个 turn 变量

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 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;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class GameGraphic  {

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

    public GameGraphic() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new GamePane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class GamePane extends JPanel {
        private int turn = 0;
        private long runtime;

        public GamePane() {
            InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
            ActionMap actionMap = getActionMap();

            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_N, 0), "next");
            actionMap.put("next", new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    System.out.println("...");
                    turn++;
                    repaint();
                }
            });

            long startTime = System.currentTimeMillis();
            Timer timer = new Timer(40, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    runtime = System.currentTimeMillis() - startTime;
                    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 width = getWidth();
            int height = getHeight();
            FontMetrics fm = g2d.getFontMetrics();
            String text = Integer.toString(turn);
            int x = (width - fm.stringWidth(text)) / 2;
            int y = ((height - fm.getHeight()) / 2) + fm.getAscent();
            g2d.drawString(text, x, y);

            text = Long.toString(runtime);
            x = width - fm.stringWidth(text);
            y = height - fm.getHeight() + fm.getAscent();
            g2d.drawString(text, x, y);
            g2d.dispose();
        }


    }
}

【讨论】:

    【解决方案2】:

    在java中有一个名为KeyListener的接口(接口是面向对象编程中的概念)

    你将 KeyListener 添加到一个对象,这里是一个例子:

    https://docs.oracle.com/javase/tutorial/uiswing/events/keylistener.html

    这是一个有 java 游戏示例的网站http://www.edu4java.com/en/game/game4.html(非常好...)

    【讨论】:

    • 嗨,MadProgrammer,您给出的示例使用了 JComponent。它仍然适用于像我这样的 JApplet 吗?因为JApplet似乎根本不接受接口!
    • @WassimGr 这归结为第二点,您应该避免覆盖顶层容器的绘制,而是使用小程序作为核心组件的容器(从 JComponentJPanel 扩展为例如),这样,您就不会将自己锁定在单个用例中(您可以将核心组件添加到您喜欢的任何容器中)
    • @MadProgrammer 我仍在学习所有这些不同组件之间的区别(尽管它们对我来说感觉相同......)但我会尝试与它们一起玩一下并弄清楚我是哪一个想用
    • @WassimGr 作为一般的经验法则,如果你想进行自定义绘画,JPanelJComponent 是首选,JApplet(和 JFrame)在它们时不会双缓冲油漆,这样你就可以得到恼人的闪烁。 JPanel 默认是不透明的,JComponent 不是
    猜你喜欢
    • 2015-04-24
    • 1970-01-01
    • 2020-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-15
    • 1970-01-01
    相关资源
    最近更新 更多