Почему actionPerformed выполняется двумя потоками?

CcmU

Создаю простую игру с качелями. Я пытаюсь обрабатывать ввод с помощью, ActionMapи InputMapпоэтому я использую, Actionчтобы сделать то, что мне нужно сделать, в этом конкретном случае переключиться в главное меню и сбросить игровую панель. Теперь я заметил, что функция showMenu()вызывается несколько раз двумя потоками, а клавиша нажимается только один раз. Почему это так? И как я могу это предотвратить?

Чтобы воспроизвести ошибку, просто запустите основную программу в режиме Startup.javaщелчка мышью, а затем нажмите mклавишу на клавиатуре; что должно воспроизводить в консоли вывод, аналогичный моему.

Вот результат с именами потоков, который я получаю:

I'M PRESSING M - THREAD: AWT-EventQueue-0
CALLING showMenu OF GUIHANLDER - THREAD AWT-EventQueue-0      
I am in stop - GameController - THREAD:AWT-EventQueue-0       
Start of reset Game - GameController - THREAD:AWT-EventQueue-0
CALLING showMenu OF GUIHANLDER - THREAD Thread-0
I am in stop - GameController - THREAD:Thread-0
Start of reset Game - GameController - THREAD:Thread-0
Created a player X: 24 Y:40
resetted
Created a player X: 49 Y:5
End of reset Game - GameController - THREAD:AWT-EventQueue-0
resetted
End of reset Game - GameController - THREAD:Thread-0
Called resetGame() - GameController - THREAD:AWT-EventQueue-0
Called resetGame() - GameController - THREAD:Thread-0
pressed M - THREAD: AWT-EventQueue-0

Вот основные области интересов:

GamePanel.java

import java.awt.Color;
import java.awt.event.ActionEvent;
import java.util.Random;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
public class GamePanel extends JPanel {

    public static final String PANEL_ID = "game_panel";

    public static final int DEFAULT_WIDTH = 100;
    public static final int DEFAULT_HEIGHT = 100;

    private int mapWidth = -1;
    private int mapHeight = -1;
    private Random r = new Random();

    public GamePanel() {
        setBackground(Color.BLACK);
        createPlayer();
        initSetup();
    }

    public void createPlayer() {
        int x = r.nextInt(100);
        int y = r.nextInt(100);
        System.out.println("Created a player X: " + x + " Y:" + y);
    }

    public void initSetup() {
        repaint();
        initInput();
    }

    private void initInput() {
        InputMap iMap = getInputMap();
        ActionMap aMap = getActionMap();

        Action goToMenu = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("I'M PRESSING M - THREAD: "+Thread.currentThread().getName());
                GUIHandler.getInstance().showMenu();
                System.out.println("pressed M - THREAD: "+Thread.currentThread().getName());
            }
        };
        aMap.put("goToMenu", goToMenu);
        iMap.put(KeyStroke.getKeyStroke('m'), "goToMenu");
    }

    public void reset() {
        if (mapHeight == -1 || mapWidth == -1) {
            mapWidth = DEFAULT_WIDTH;
            mapHeight = DEFAULT_HEIGHT;
        }
        createPlayer();
        initSetup();
        System.out.println("resetted");
    }

}

GameController.java

public class GameController {

    private GamePanel gamePanel;
    private GameLoop gameLoop;

    private static GameController instance = null;

    private GameController() {
        gamePanel = GUIHandler.getInstance().getGamePanel();

        gameLoop = new GameLoop();
    }

    public void start() {
        if (!gameLoop.isGameRunning())
            gameLoop.run();
    }

    public void stop() {
        System.out.println("I am in stop - GameController - THREAD:"+Thread.currentThread().getName());
        if (gameLoop.isGameRunning())
            gameLoop.stop();
        resetGame();
        System.out.println("Called resetGame() - GameController - THREAD:"+Thread.currentThread().getName());
    }

    private void resetGame() {
        System.out.println("Start of reset Game - GameController - THREAD:"+Thread.currentThread().getName());
        gamePanel.reset();
        System.out.println("End of reset Game - GameController - THREAD:"+Thread.currentThread().getName());
    }

    public GamePanel getGamePanel() {
        return gamePanel;
    }

    public static GameController getInstance() {
        if (instance == null)
            instance = new GameController();
        return instance;
    }

}

GameLoop.java


public class GameLoop {

    private static final String STOPPED = "stopped";
    private static final String RUNNING = "running";
    private static final int FPS_GOAL = 60;
    private static final int MS_PER_FRAME = 1000/FPS_GOAL;
    
    protected String status;
    private Thread gameThread;

    protected GameLoop() {
        status = STOPPED;
    }

    public void run() {
        status = RUNNING;
        gameThread = new Thread(this::processGameLoop);
        gameThread.start();
    }

    public void stop() {
        status = STOPPED;
        gameThread.interrupt();
        gameThread = null;
    }

    public boolean isGameRunning() {
        return status == RUNNING;
    }

    private void render()   {
        GameController.getInstance().getGamePanel().repaint();
    }

    protected void processGameLoop()    {
        while (isGameRunning())
        {
            long start = System.currentTimeMillis();
            render();

            try {
                long tiemout = start + MS_PER_FRAME - System.currentTimeMillis();
                if (tiemout > 0)
                    Thread.sleep(tiemout);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                GUIHandler.getInstance().showMenu();
            }
        }

    }
}

GUIHandler.java

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Dimension;
public class GUIHandler {

    private JPanel contentPane;
    private Menu menu;
    private GamePanel game;

    public static final Dimension PREFERRED_DIMENSION = new Dimension(1200, 800);

    private static GUIHandler instance = null;

    private GUIHandler()    {}
    
    public void displayGUI()    {
        JFrame frame = new JFrame("Square Territory");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        contentPane = new JPanel();
        contentPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        contentPane.setLayout(new CardLayout());
        
        menu = new Menu();
        game = new GamePanel();

        contentPane.add(menu, Menu.PANEL_ID);
        contentPane.add(game, GamePanel.PANEL_ID);

        frame.setPreferredSize(PREFERRED_DIMENSION);
        frame.getContentPane().add(contentPane, BorderLayout.CENTER);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    public GamePanel getGamePanel() {return game;}

    public void startGame() {
        ((CardLayout) contentPane.getLayout()).show(contentPane, GamePanel.PANEL_ID);
        game.grabFocus();
        GameController.getInstance().start();
    }
    public void showMenu()  {
        System.out.println("CALLING showMenu OF GUIHANLDER - THREAD "+Thread.currentThread().getName());
        GameController.getInstance().stop();
        ((CardLayout) contentPane.getLayout()).show(contentPane, Menu.PANEL_ID);
    }

    public static GUIHandler getInstance()  {
        if (instance == null)
            instance = new GUIHandler();
        return instance;
    }

}

Menu.java

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JTextPane;
import javax.swing.SpinnerModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.JSpinner.NumberEditor;
import javax.swing.event.MouseInputAdapter;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;

import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.event.MouseEvent;
import java.awt.BorderLayout;
import java.awt.Dimension;

public class Menu extends JPanel {

    public static final String PANEL_ID = "menu_panel";

    /**
     * Creating the menu and its components.
     */
    public Menu() {
        setOpaque(true);
        setBackground(Color.CYAN.darker());
        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));

        JTextPane logo = new JTextPane();
        logo.setText("SquareTerritory");
        logo.setFont(new Font(Font.SANS_SERIF, Font.ITALIC + Font.BOLD, 60));
        SimpleAttributeSet center = new SimpleAttributeSet();
        StyleConstants.setAlignment(center, StyleConstants.ALIGN_CENTER);
        logo.getStyledDocument().setParagraphAttributes(0, logo.getStyledDocument().getLength(), center, false);
        logo.setBackground(Color.CYAN.darker());
        logo.setEditable(false);
        logo.setFocusable(false);

        JPanel dimensionChoose = new JPanel(new BorderLayout());
        SpinnerModel widthModel = new SpinnerNumberModel(100, 10, 800, 1);
        JSpinner width = new JSpinner(widthModel);
        SpinnerModel heightModel = new SpinnerNumberModel(100, 10, 800, 1);
        JSpinner height = new JSpinner(heightModel);
        width.getEditor().setPreferredSize(new Dimension(200, 50));
        width.setToolTipText("Width of the grid");
        ((NumberEditor) width.getEditor()).getTextField().setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 40));
        ((NumberEditor) width.getEditor()).getTextField().setBackground(Color.CYAN.darker().darker());

        height.getEditor().setPreferredSize(new Dimension(200, 50));
        height.setToolTipText("Height of the grid");
        ((NumberEditor) height.getEditor()).getTextField().setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 40));
        ((NumberEditor) height.getEditor()).getTextField().setBackground(Color.CYAN.darker().darker());

        JPanel labelPane = new JPanel();
        JLabel widthLabel = new JLabel("Width");
        widthLabel.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 40));
        JLabel heightLabel = new JLabel("Height");
        heightLabel.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 40));

        labelPane.setBackground(Color.CYAN.darker());
        labelPane.add(widthLabel);
        labelPane.add(Box.createGlue());
        labelPane.add(heightLabel);

        widthLabel.setLabelFor(width);
        heightLabel.setLabelFor(height);
        dimensionChoose.add(width, BorderLayout.WEST);
        dimensionChoose.add(labelPane);
        dimensionChoose.add(height, BorderLayout.EAST);

        JButton play = new JButton("Play");
        play.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 40));
        play.setForeground(Color.WHITE);
        play.setBackground(Color.DARK_GRAY);
        play.addMouseListener(new MouseInputAdapter() {
            @Override
            public void mouseEntered(MouseEvent e) {
                play.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 40));
                play.setBackground(Color.LIGHT_GRAY);
            }
            
            @Override
            public void mouseExited(MouseEvent e) {
                play.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 40));
                play.setBackground(Color.DARK_GRAY);
            }
        });
        play.addActionListener(e -> GUIHandler.getInstance().startGame());

        JButton exit = new JButton("Exit");
        exit.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 40));
        exit.setForeground(Color.WHITE);
        exit.setBackground(Color.DARK_GRAY);
        exit.addMouseListener(new MouseInputAdapter() {
            @Override
            public void mouseEntered(MouseEvent e) {
                exit.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 40));
                exit.setBackground(Color.LIGHT_GRAY);
            }
            
            @Override
            public void mouseExited(MouseEvent e) {
                exit.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 40));
                exit.setBackground(Color.DARK_GRAY);
            }
        });
        exit.addActionListener(e -> System.exit(0));

        logo.setAlignmentX(Component.CENTER_ALIGNMENT);
        logo.setMaximumSize(new Dimension(logo.getMaximumSize().width, 500));
        dimensionChoose.setAlignmentX(Component.CENTER_ALIGNMENT);
        dimensionChoose.setMaximumSize(new Dimension(800, 200));
        dimensionChoose.setBackground(Color.CYAN.darker());
        play.setAlignmentX(Component.CENTER_ALIGNMENT);
        exit.setAlignmentX(Component.CENTER_ALIGNMENT);

        add(logo);
        add(Box.createVerticalGlue());
        add(dimensionChoose);
        add(Box.createVerticalGlue());
        add(play);
        add(Box.createVerticalGlue());
        add(exit);
    }

}

Startup.java

public class Startup {
    public static void main(String[] args) {
        GUIHandler gh = GUIHandler.getInstance();
        gh.displayGUI();
    }
}
Судно на воздушной подушке, полное угрей

Ваш второй вызов showMenu()выполняется здесь, в вашем «игровом цикле», а не из-за того, что действие вызывается дважды:

protected void processGameLoop() {
    while (isGameRunning()) {
        long start = System.currentTimeMillis();
        render();

        try {
            long tiemout = start + MS_PER_FRAME - System.currentTimeMillis();
            if (tiemout > 0)
                Thread.sleep(tiemout);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();

            // ** println inserted by me **
            System.out.println("thread interrupted");

            GUIHandler.getInstance().showMenu(); // **** here ****
        }
    }
}

Вызывается блок catch и выполняется код внутри него. Ваш игровой цикл не является потокобезопасным для Swing, поскольку вы выполняете вызовы Swing из фонового потока.

Это код, который прерывает:

class GameLoop {

    // ....

    public void stop() {
        status = STOPPED;
        gameThread.interrupt();  // *****
        gameThread = null;
    }

который вызывается:

class GameController {

    // ....

    public void stop() {
        System.out.println("I am in stop - GameController - THREAD:" + Thread.currentThread().getName());
        if (gameLoop.isGameRunning())
            gameLoop.stop();  // *****
        resetGame();
        System.out.println("Called resetGame() - GameController - THREAD:" + Thread.currentThread().getName());
    }

который вызывается самим showMenu:

public void showMenu() {
    System.out.println("CALLING showMenu OF GUIHANLDER - THREAD " + Thread.currentThread().getName());
    GameController.getInstance().stop();  // *****
    ((CardLayout) contentPane.getLayout()).show(contentPane, Menu.PANEL_ID);
}

Эта статья взята из Интернета, укажите источник при перепечатке.

Если есть какие-либо нарушения, пожалуйста, свяжитесь с[email protected] Удалить.

Отредактировано в
0

я говорю два предложения

0обзор
Войти в системуУчаствуйте в комментариях

Статьи по теме

Как «обновить» в JFrame внутри actionperformed метода?

Java ActionPerformed () метод не выполняется

BeanCurrentlyInCreationException для actionPerformed (AWT)

Как метод actionPerformed вызывается без явного вызова?

Java - текст JButton исчезает, если после этого было определено действие actionPerformed

Java: метод actionPerformed не срабатывает при нажатии кнопки

Как изменить задержку таймера качания внутри actionPerformed ()

Могу ли я добавить ActionListener к кнопке в jFrame и иметь метод actionPerformed в другом классе?

Как изменить задержку таймера качания внутри actionPerformed ()

KeyBindings застряли на actionPerformed ()

Минимизация / развертывание java swing gui выполняет метод actionPerformed

Вызов нелокальной переменной в ActionPerformed

Главное должно быть абстрактным, проблема метода ActionPerformed с Listener

GUI ActionPerformed () создает ошибку

Почему я получаю ошибку actionPerformed (): неизвестный источник?

use controller in actionPerformed method - needs to be final?

Как добавить значения в список целочисленных массивов с помощью метода actionPerformed и jbutton?

Как я могу реализовать этот метод Array.sort в моем actionPerformed?

actionListener взаимодействует с методом actionPerformed, не работающим с классом Timer

Создание нового JFrame внутри метода actionPerformed (), а затем как я могу выполнить действие с помощью этого JFrame JButton

Как вызвать метод actionPerformed из другого класса в java

Как передать индекс цикла внутри ActionListener'а actionPerformed [Java]

Если инструкция не выполняется во время события ActionPerformed

Каков правильный синтаксис для actionPerformed (ActionEvent e)?

Как заставить ActionListener выполнять разные события в зависимости от actionPerformed?

ActionPerformed вызывается при щелчке JComboBox в ячейке Jtable

SearchEventListener не является абстрактным и не переопределяет абстрактный метод actionPerformed

Как вызвать метод ActionPerformed из другого класса

Как приостановить программу для метода actionPerformed

TOP список

  1. 1

    Распределение Рэлея Curve_fit на Python

  2. 2

    Merging legends in plotly subplot

  3. 3

    Невозможно отобразить данные модели загрузки Spring в Thymeleaf

  4. 4

    Définition de la valeur par défaut dans le dictionnaire Python si la clé est manquante

  5. 5

    В типе Observable <unknown> отсутствуют следующие свойства из типа Promise <any>.

  6. 6

    Как я могу нарисовать заполненный прямоугольник в JFreeChart?

  7. 7

    Как удалить круглые скобки из количества продуктов подкатегории WooCommerce

  8. 8

    TypeError: store.getState não é uma função. (Em 'store.getState ()', 'store.getState' é indefinido, como posso resolver esse problema?

  9. 9

    Чтение данных по строкам в Python с использованием openpyxl

  10. 10

    FormsAuthentication.SignOut () не работает после изменения CookieDomain

  11. 11

    Does addListener in JavaFX get garbage collected when the ChangeListener is typed as a lambda?

  12. 12

    ContentDialog.showAsync в универсальном оконном приложении Win 10

  13. 13

    Как изменить ширину UIPickerView с помощью нескольких средств выбора в SwiftUI?

  14. 14

    Перебирайте несколько столбцов в фрейме данных Panda и находите уникальные значения подсчета

  15. 15

    Обновление строк и столбцов с помощью openpyxl из python

  16. 16

    PayPal REST API возвращает INVALID_CURRENCY_AMOUNT_FORMAT

  17. 17

    проверить повторяющееся значение и входное значение с помощью python openpyxl

  18. 18

    Ошибка при доступе к файлу по пути с использованием модуля python openpyxl

  19. 19

    Cannot find reference System

  20. 20

    Ошибка: недопустимый CSS после "body": ожидаемый селектор или at-правило, было "{" в строке 4

  21. 21

    Android Включение / выключение вспышки камеры программно с помощью Camera2

популярныйтег

файл