簡體   English   中英

從 JFrame 調用時,ActionListener 在 JPanel 上執行兩次

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

我希望為我的吃豆人精靈設置動畫,但是當我的計時器調用actionPerformed來更新圖像時,它會執行兩次並跳過動畫。

下面是主類的代碼:

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();
    }
}

這是游戲類的代碼:

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 + "    ");
    }
}

如果我單獨運行游戲類,動作監聽器工作正常並且只執行一次,但是從我的主類調用動作監聽器執行兩次並且動畫開始跳幀。

你有兩個Timer ,每個都觸發repaint和......歡迎來到你的架構錯誤的奇妙世界。

  1. 不要覆蓋像JFrame這樣的頂級容器的繪制,事實上,通常不鼓勵從JFrame擴展,因為您實際上並沒有向類添加新功能,而且它們已經是一堆組件。
  2. 避免過度使用static 如果您發現您正在使用static來允許您引用其他對象,那么您的設計是錯誤的。 您應該通過構造函數或 getter 將信息傳遞給您的類。 這就是“模型-視圖-控制器”之類的東西進來並且很重要的地方
  3. 繪畫只是繪畫。 你永遠不應該執行可能改變組件狀態的邏輯。 Paint 應該簡單地繪制當前狀態,因為一個繪制通道可能會在任何時候執行,很多時候你不需要做任何事情來觸發它。
  4. 一般來說, KeyListener是一個糟糕的選擇,你應該使用Key bindings API來代替。

您應該關注的第一件事是將游戲核心邏輯與渲染工作流程分離。 這意味着您將有一個“主循環”(在本例中為 Swing Timer ),它將更新模式(或狀態),然后觸發一個繪制周期。 然后渲染工作流將使用當前“狀態”進行渲染。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM