variável de classe não atualizando após chamada de método

Dec 01 2020

Eu sou muito novo em Java e só depois de pesquisar, pesquisar e ler muitas respostas, estou postando isso. Estou meio perdido. Um pouco de orientação seria de grande ajuda. A seguir está um método de uma classe que implementa a interface "ActionListener". O que estou tentando fazer é o seguinte: Existe um botão no qual um clicado deve abrir uma nova janela com duas opções na forma de dois botões de rádio. Preciso saber o botão de rádio que foi selecionado para uso posterior em meu código. Eu declarei a variável "scoreOption" como uma variável de classe e estática, e então tento atualizá-la no método abstrato "actionPerformed". Mas quando me refiro a ele (após a chamada do método), o valor permanece o mesmo - nulo, ou o que quer que eu tenha definido inicialmente. Aqui está o código:

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

Classe de jogo principal onde o sistema de pontuação do método é chamado.

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

}

Além disso, você poderia me informar sobre as seguintes questões:

  1. Recomenda-se a implementação de "ActionListener" em outro "ActionListener"? Boa prática?
  2. Pode haver apenas uma declaração do método "actionPerformed" ou ele também pode estar sobrecarregado?
  3. É possível obter um valor de retorno do método "actionPerformed"?

Eu ficaria muito grato se algumas dicas pudessem ser fornecidas. Eu tentei muito mesmo e só depois postei aqui. Muito obrigado antecipadamente.

Pequena edição: Quando eu "System.out.println" o "actioncommand" lá, ele funciona perfeitamente, imprimindo no console. Mas não quando tento atualizar a variável de classe e depois tento imprimi-la após a chamada do método. Não sei se isso ajuda.

Respostas

1 HovercraftFullOfEels Dec 01 2020 at 10:30

Os JFrames não são modais - você cria um e o exibe, ele não bloqueia o fluxo do código e, portanto, está extraindo o valor de scoreOption no momento em que o JFrame está sendo exibido e antes que o usuário tenha a chance de alterá-lo. Você precisa usar um diálogo modal como um JDialog que é criado como um diálogo modal ou usar um JOptionPane (que na verdade é apenas um JDialog modal por baixo do capô). Isso bloqueará o fluxo de código para que você extraia os dados somente depois que eles forem alterados pelo usuário.

Um exemplo que prova o ponto:

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

Se você executar o código, ao mostrar o sub JFrame, o texto de teste será exibido imediatamente no console antes que a caixa de diálogo seja tratada. Se você pressionar o botão para mostrar a caixa de diálogo, a exibição do texto de teste será atrasada até que o botão seja pressionado, alterando o texto.

Pressionar o botão moldura duas vezes finalmente mostrará o texto correto, pois o texto foi definido na primeira vez em que foi exibido.

1 camickr Dec 01 2020 at 11:00

Um JDialig é como um JFrame. Ou seja, você adiciona componentes a ele como faria com qualquer quadro.

A diferença é que você pode fazer um modal JDialog. Isso significa que quando você usa:

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

O código após a instrução setVisible (...) não será executado até que a caixa de diálogo seja fechada. Isso também significa que você não pode clicar no JFrame pai até que a caixa de diálogo seja fechada.

Uma maneira fácil de criar um modal JDialogé usar a JOptionPane. Possui alguns métodos predefinidos que facilitam a solicitação de entrada do usuário.

Por exemplo, no seu caso, você poderia fazer algo como:

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

O acima também é um exemplo de um "MRE". O código é simples e está contido em uma única classe que você pode copiar / colar / compilar e testar.

Leia a seção do tutorial do Swing sobre como usar caixas de diálogo para obter mais exemplos de como usar a JOptionPane.

Se você realmente deseja usar botões de opção, pode criar um painel com os botões de opção e exibi-los no painel de opção usando o showConfirmDialog(...)método. Quando a caixa de diálogo fechar, você precisará obter o comando de ação ButtonModeldo ButtonGroup.

Consulte: como definir e gerenciar o layout do JOptionPane para obter um exemplo básico desta abordagem para você começar.