переменная класса не обновляется после вызова метода

Dec 01 2020

Я новичок в Java, и только после исследования, поиска в Google и прочтения множества ответов я публикую это. Я немного заблудился. Небольшое руководство было бы большим подспорьем. Ниже приводится метод из класса, реализующего интерфейс «ActionListener». Я пытаюсь сделать следующее: есть кнопка, нажатие которой должно открывать новое окно с двумя вариантами в виде двух радиокнопок. Мне нужно знать переключатель, который был выбран для дальнейшего использования в моем коде. Я объявил переменную "scoreOption" как статическую и переменную класса, а затем попытался обновить ее в абстрактном методе "actionPerformed". Но когда я обращаюсь к нему (после вызова метода), значение остается прежним - null или то, что я установил изначально. Вот код:

import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Scanner;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextArea;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class StartEvents implements ActionListener {
    StartPanel startingPanel;
    static String scoreOption;
    
    public StartEvents(StartPanel startPanel) {
        startingPanel = startPanel;
    }
    // Scoring System Window - 1
    public void scoringSystem() {
        startingPanel.scoringSystem.addActionListener(new ActionListener () {
            @Override
            public void actionPerformed(ActionEvent e) {
                Panel scoringSystemPanel = new Panel();
                JFrame scoreSystemFrame  = scoringSystemPanel.frame(150, 250, "Scoring System", 2, true);
                JPanel scoreSystemPanel = scoringSystemPanel.panel(Color.lightGray);
                JButton confirmSelection = scoringSystemPanel.button(40, 20, "Confirm");
                JRadioButton scoreSystem1 = scoringSystemPanel.radioButton("Option 1: Same Points Per Hit");    
                scoreSystem1.setActionCommand("Option 1");
                JRadioButton scoreSystem2 = scoringSystemPanel.radioButton("Option 2: Unique Points Per Hit");
                scoreSystem2.setActionCommand("Option 2");
                ButtonGroup scoreSys = new ButtonGroup();
                scoreSys.add(scoreSystem1);
                scoreSys.add(scoreSystem2);
                scoreSystemFrame.getContentPane().add(scoreSystemPanel);
                scoreSystemPanel.add(scoreSystem1);
                scoreSystemPanel.add(scoreSystem2);
                scoreSystemPanel.add(confirmSelection);
                
                // Get Selection Event
                // Option 1
                scoreSystem1.addActionListener(new ActionListener () {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        if (scoreSystem1.isSelected()) {
                            scoreOption = scoreSystem1.getActionCommand();
                        }
                    }
                });
                // Option 2
                scoreSystem2.addActionListener(new ActionListener () {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        if (scoreSystem2.isSelected()) {
                            scoreOption = scoreSystem2.getActionCommand();
                        }
                    }
                });
                // Confirm Event 
                confirmSelection.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        scoreSystemFrame.dispose();
                    }
                });
            }
        });
    }

Основной класс игры, в котором вызывается система оценки методов.

import java.util.ArrayList;

public class Game {

    public static void main(String[] args) {
        StartPanel startingPanel = new StartPanel();
        startingPanel.makeStartPanel();
        StartEvents starter = new StartEvents(startingPanel);
        starter.rulesButton();
        starter.exitButton();
        starter.highScoresButton();
        ArrayList<Integer> dimensions = starter.boardSizeSelector();
        
        // Problem Zone
        System.out.println(StartEvents.scoreOption);
        starter.scoringSystem();
        System.out.println(StartEvents.scoreOption);
        // The two values of scoreOption should be different
        
        String[] playPanelDetails = {"970", "Player 1", "450"};
        
        // Final Start of the Game
        starter.startGameButton(playPanelDetails, dimensions);
        
    }

}

Кроме того, не могли бы вы сообщить мне о следующих вопросах:

  1. Рекомендуется ли реализация ActionListener в другом ActionListener? Хорошая практика?
  2. Может ли быть только одно объявление метода actionPerformed или его тоже можно перегрузить?
  3. Можно ли получить возвращаемое значение из метода actionPerformed?

Буду очень признателен, если можно будет дать хоть какие-то подсказки. Я очень много перепробовала и только потом выложила сюда. Заранее большое спасибо.

Небольшое редактирование: когда я "System.out.println" сам "actioncommand", он работает отлично, печатая в консоли. Но не тогда, когда я пытаюсь обновить переменную класса, а затем пытаюсь распечатать ее после вызова метода. Не знаю, поможет ли это.

Ответы

1 HovercraftFullOfEels Dec 01 2020 at 10:30

JFrames не являются модальными - вы создаете их и отображаете, они не блокируют поток кода, и поэтому вы извлекаете значение scoreOption прямо в момент отображения JFrame и до того, как пользователь сможет его изменить. Вам нужно использовать модальный диалог, такой как JDialog, который создается как модальный диалог, или использовать JOptionPane (который на самом деле является просто модальным JDialog под капотом). Это заблокирует поток кода, так что вы будете извлекать данные только после того, как они были изменены пользователем.

Пример, подтверждающий это:

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

public class FooGui01 extends JPanel {
    private String frameTest = "";
    private String dialogTest = "";
    private JFrame mainFrame = new JFrame("Main GUI");
    
    private JFrame subFrame;
    private JDialog dialog;

    
    public FooGui01() {
        JButton showFrameBtn = new JButton("Show JFrame");
        showFrameBtn.addActionListener(e -> {
            changeTest1WithJFrame();
            System.out.println("frameTest: " + frameTest);
        });
        
        JButton showDialogBtn = new JButton("Show JDialog");
        showDialogBtn.addActionListener(e -> {
            changeTest2WithModalDialog();
            System.out.println("dialogTest: " + dialogTest);
        });
        
        JPanel panel = new JPanel();
        panel.add(showDialogBtn);
        panel.add(showFrameBtn);
        
        mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        mainFrame.add(panel);
        mainFrame.pack();
        mainFrame.setLocationByPlatform(true);
        mainFrame.setVisible(true);
        
    }
    
    public void changeTest1WithJFrame() {

        if (subFrame == null) {
            subFrame = new JFrame("Frame");
            JButton button = new JButton("Press me");
            button.addActionListener(e -> {
                frameTest = "Hello World and frameTest";
                subFrame.setVisible(false);
            });

            JPanel panel = new JPanel();
            panel.add(button);
            
            subFrame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
            subFrame.add(panel);
            subFrame.pack();
            subFrame.setLocationByPlatform(true);
        }
        subFrame.setVisible(true);
    }
    
    public void changeTest2WithModalDialog() {
        
        if (dialog == null) {       
            dialog = new JDialog(mainFrame, "Dialog", Dialog.ModalityType.APPLICATION_MODAL);
            JButton button = new JButton("Press me");
            button.addActionListener(e -> {
                dialogTest = "Hello World and dialogTest";
                dialog.setVisible(false);
            });

            JPanel panel = new JPanel();
            panel.add(button);
            
            dialog.add(panel);
            dialog.pack();
            dialog.setLocationByPlatform(true);
        }
        dialog.setVisible(true);
    }
        
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new FooGui01());
    }
}

Если вы запустите код, когда вы показываете вспомогательный JFrame, тестовый текст отображается сразу в консоли до того, как диалог будет обработан. Если вы нажмете кнопку, чтобы отобразить диалоговое окно, отображение тестового текста будет отложено до тех пор, пока не будет нажата кнопка, что изменит текст.

При двойном нажатии кнопки рамки будет показан правильный текст, поскольку текст был установлен при первом отображении.

1 camickr Dec 01 2020 at 11:00

JDialig похож на JFrame. То есть вы добавляете к нему компоненты, как и любой фрейм.

Разница в том, что вы можете создать модальный JDialog. Это означает, что при использовании:

dialog.setVisible(true);
System.out.println("here");

Код после оператора setVisible (...) не будет выполняться до закрытия диалогового окна. Это также означает, что вы не можете щелкнуть родительский JFrame, пока диалоговое окно не будет закрыто.

Самый простой способ создать modal JDialog- использовать JOptionPane. В нем есть несколько предопределенных методов, которые упрощают ввод данных пользователем.

Например, в вашем случае вы можете сделать что-то вроде:

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

public class SSCCE extends JPanel
{
    private int scoringOption = -1;

    public SSCCE()
    {
        JButton button = new JButton("Change Points Option");
        add(button);

        button.addActionListener((e) -> displayOptionDialog());
    }

    private void displayOptionDialog()
    {
        Window window = SwingUtilities.windowForComponent( this );

        // Custom button text

        Object[] options = {"Option 1: Same Points Per Hit", "Option 2: Unique Points Per Hit"};

        scoringOption = JOptionPane.showOptionDialog(
            window,
            "Select your scoring option:",
            "Scoring Option",
            JOptionPane.YES_NO_CANCEL_OPTION,
            JOptionPane.QUESTION_MESSAGE,
            null,
            options,
            null);

        System.out.println( scoringOption );
    }

    private static void createAndShowGUI()
    {
        JFrame frame = new JFrame("SSCCE");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new SSCCE());
        frame.pack();
        frame.setLocationByPlatform( true );
        frame.setVisible( true );
    }

    public static void main(String[] args) throws Exception
    {
        java.awt.EventQueue.invokeLater( () -> createAndShowGUI() );
    }
}

Вышеупомянутое также является примером «ИМО». Код прост и содержится в одном классе, который вы можете копировать / вставлять / компилировать и тестировать.

Прочтите раздел учебника Swing о том, как использовать диалоги, чтобы получить дополнительные примеры использования файла JOptionPane.

Если вы действительно хотите использовать переключатели, вы можете создать панель с переключателями и отобразить их на панели параметров с помощью этого showConfirmDialog(...)метода. Когда диалоговое окно закроется, вам нужно будет получить команду действия из ButtonModelфайла ButtonGroup.

Смотрите: как установить и управлять макетом JOptionPane, чтобы получить базовый пример этого подхода, чтобы вы начали.