简体   繁体   English

从 JFrame 调用时,ActionListener 在 JPanel 上执行两次

[英]ActionListener executes twice on JPanel when called from JFrame

I wish to animate my pac-man sprite however when my timer calls actionPerformed to update the image, it executes twice and skips over an animation.我希望为我的吃豆人精灵设置动画,但是当我的计时器调用actionPerformed来更新图像时,它会执行两次并跳过动画。

Here is the code for the main class:下面是主类的代码:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.concurrent.TimeUnit;
import javax.swing.Timer;

public class Pacman extends JFrame implements ActionListener, KeyListener{
    public static Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();  
    public static int FRAME_HEIGHT = (int)(dim.height-dim.height/5);
    public static int FRAME_WIDTH = (int)(dim.width-dim.width/5);
    public static Pacman frame = new Pacman();
    public static Game gameInterface = new Game();
    public static Timer timer;

    public Pacman() {
        super("Pac-man");
        timer = new Timer(175, this);
        timer.start();
        addKeyListener(this);
    }

    public static void main(String[] args) {
        ImageIcon frameIcon = new ImageIcon("Icon.png");

        frame.setSize(FRAME_WIDTH,FRAME_HEIGHT);
        frame.setIconImage(frameIcon.getImage());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
        frame.setResizable(false);

        frame.setContentPane(gameInterface);
        gameInterface.requestFocus();
        gameInterface.setFocusable(true);


        Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();    
        int JframeX = (dim.width-FRAME_WIDTH)/2;
        int JframeY = (dim.height-FRAME_HEIGHT)/2;
        frame.setLocation(JframeX, JframeY);
    }

    public void keyTyped(KeyEvent e) {
    }

    public void keyPressed(KeyEvent e) {
    }

    public void keyReleased(KeyEvent e) {
    }

    public void paint(Graphics g) {
        super.paint(g);     
    }

    public void actionPerformed(ActionEvent ev){
        repaint();
    }
}

Here is the code for the game class:这是游戏类的代码:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.concurrent.TimeUnit;
import javax.swing.Timer;

public class Game extends JPanel implements ActionListener, KeyListener{
    public static Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();  
    public static int FRAME_HEIGHT = (int)(dim.height-dim.height/5);
    public static int FRAME_WIDTH = (int)(dim.width-dim.width/5);
    public static Game gamePanel = new Game();
    public static Timer timer;
    public static boolean gameExit = false;
    public static int animCounter = 0, imgPacmanX1 = 840, imgPacmanY1 = 0, imgPacmanX2 = 890, imgPacmanY2 = 50;

    public Game() {
        timer = new Timer(500, this);
        timer.start();      
        addKeyListener(this);
    }

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

    public void keyTyped(KeyEvent e) {
    }

    public void keyPressed(KeyEvent e) {
    }

    public void keyReleased(KeyEvent e) {
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        ImageIcon animations = new ImageIcon("Pacman_Animations.png");

        g.setColor(Color.BLUE);
        g.fillRect(0, 0, FRAME_WIDTH, FRAME_HEIGHT);
        g.drawImage(animations.getImage(), 150, 200, 200, 250, imgPacmanX1, imgPacmanY1, imgPacmanX2, imgPacmanY2, null);
    }

    public void actionPerformed(ActionEvent ev){
        if(animCounter == 2){
            imgPacmanY1 = 0;
            imgPacmanY2 = 50;
            animCounter = 0;
        }
        else{
            imgPacmanY1 += 50; 
            imgPacmanY2 += 50;
            animCounter += 1;
        }
        repaint();
        System.out.print(imgPacmanY1 + "   " + imgPacmanY2 + "    ");
    }
}

If I run the game class separately, the action listener works fine and only executes once, however called from my main class the action listener executes twice and the animations starts skipping frames.如果我单独运行游戏类,动作监听器工作正常并且只执行一次,但是从我的主类调用动作监听器执行两次并且动画开始跳帧。

You have two Timer 's each one is trigger a repaint and ... welcome to the wonderful world of why your architecture is wrong.你有两个Timer ,每个都触发repaint和......欢迎来到你的架构错误的奇妙世界。

  1. Don't override paint of top level containers like JFrame , in fact, extending from JFrame is generally discouraged as you're not actually adding a new functionality to the class and they are a jumble of components already.不要覆盖像JFrame这样的顶级容器的绘制,事实上,通常不鼓励从JFrame扩展,因为您实际上并没有向类添加新功能,而且它们已经是一堆组件。
  2. Avoid over use of static .避免过度使用static If you find you're using static to allow you to reference other objects, then your design is wrong.如果您发现您正在使用static来允许您引用其他对象,那么您的设计是错误的。 You should be passing information to your class via the constructor or getters.您应该通过构造函数或 getter 将信息传递给您的类。 This is where things like "model-view-controller" come in and are important这就是“模型-视图-控制器”之类的东西进来并且很重要的地方
  3. Painting is painting only.绘画只是绘画。 You should never be performing logic which might change the state of the component.你永远不应该执行可能改变组件状态的逻辑。 Paint should simply paint the current state, as a paint pass might be executed any time, many of the times without your doing anything to trigger it. Paint 应该简单地绘制当前状态,因为一个绘制通道可能会在任何时候执行,很多时候你不需要做任何事情来触发它。
  4. Generally speaking, KeyListener is a poor choice, you should be using the Key bindings API instead.一般来说, KeyListener是一个糟糕的选择,你应该使用Key bindings API来代替。

The first thing you should focus on is decoupling the game core logic from the rendering work flow.您应该关注的第一件事是将游戏核心逻辑与渲染工作流程分离。 This means you'd have a "main loop" (in this case, the Swing Timer ) which would update the mode (or state) and then trigger a paint cycle.这意味着您将有一个“主循环”(在本例中为 Swing Timer ),它将更新模式(或状态),然后触发一个绘制周期。 The rendering work flow would then use the current "state" for its rendering.然后渲染工作流将使用当前“状态”进行渲染。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM