zmienna klasy nie aktualizuje się po wywołaniu metody

Dec 01 2020

Jestem całkiem nowy w Javie i dopiero po zbadaniu, googlowaniu i przeczytaniu wielu odpowiedzi, publikuję to. Jestem zagubiony. Małe wskazówki byłyby bardzo pomocne. Poniżej przedstawiono metodę z klasy, która implementuje interfejs „ActionListener”. To, co próbuję zrobić, to: Jest przycisk, którego kliknięcie powinno otworzyć nowe okno z dwiema opcjami w postaci dwóch przycisków radiowych. Muszę znać przycisk radiowy, który został wybrany do dalszego wykorzystania w moim kodzie. Zadeklarowałem zmienną „scoreOption” jako zmienną klasową i statyczną, a następnie próbowałem zaktualizować ją w metodzie abstrakcyjnej „actionPerformed”. Ale kiedy odnoszę się do tego (po wywołaniu metody), wartość pozostaje taka sama - null lub cokolwiek ustawiłem na początku. Oto kod:

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

Główna klasa gier, w której wywoływana jest metoda scoringsystem.

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

}

Czy mógłbyś również dać mi znać w przypadku następujących pytań:

  1. Zalecane jest wdrożenie „ActionListener” w innym „ActionListener”? Dobra praktyka?
  2. Czy może istnieć tylko jedna deklaracja metody „actionPerformed”, czy też może być ona przeciążona?
  3. Czy można uzyskać wartość zwracaną z metody „actionPerformed”?

Byłbym naprawdę wdzięczny, gdyby można było udzielić choćby kilku wskazówek. Naprawdę bardzo się starałem i dopiero wtedy opublikowałem to tutaj. Z góry bardzo dziękuję.

Mała edycja: Kiedy „System.out.println” znajduje się w „komendzie akcji”, działa ona doskonale, drukując w konsoli. Ale nie wtedy, gdy próbuję zaktualizować zmienną klasy, a następnie próbuję ją wydrukować po wywołaniu metody. Nie wiem, czy to pomaga.

Odpowiedzi

1 HovercraftFullOfEels Dec 01 2020 at 10:30

Ramki JFrame nie są modalne - tworzysz je i wyświetlasz, nie blokuje to przepływu kodu, więc wyodrębniasz wartość scoreOption w czasie, gdy JFrame jest wyświetlana i zanim użytkownik miał szansę ją zmienić. Musisz użyć modalnego okna dialogowego, takiego jak JDialog, które jest tworzone jako modalne okno dialogowe, lub użyć JOptionPane (który w rzeczywistości jest po prostu modalnym JDialog pod maską). Spowoduje to zablokowanie przepływu kodu, dzięki czemu wyodrębnisz dane dopiero po ich zmianie przez użytkownika.

Przykład, który to potwierdza:

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

Jeśli uruchomisz kod, po wyświetleniu podrzędnej ramki JFrame tekst testowy zostanie wyświetlony bezpośrednio w konsoli, zanim zajmiemy się dialogiem. Jeśli naciśniesz przycisk, aby wyświetlić okno dialogowe, wyświetlanie tekstu testowego zostanie opóźnione do momentu naciśnięcia przycisku, co spowoduje zmianę tekstu.

Dwukrotne naciśnięcie przycisku ramki wyświetli ostatecznie prawidłowy tekst, ponieważ tekst został ustawiony przy pierwszym wyświetleniu.

1 camickr Dec 01 2020 at 11:00

JDialig jest jak JFrame. Oznacza to, że dodajesz do niego komponenty, tak jak każdą ramkę.

Różnica polega na tym, że możesz utworzyć modal JDialog. Oznacza to, że kiedy używasz:

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

Kod następujący po instrukcji setVisible (...) nie zostanie wykonany do momentu zamknięcia okna dialogowego. Oznacza to również, że nie możesz kliknąć nadrzędnej ramki JFrame, dopóki okno dialogowe nie zostanie zamknięte.

Łatwym sposobem na utworzenie pliku modal JDialogjest użycie pliku JOptionPane. Ma kilka predefiniowanych metod, które ułatwiają monitowanie o wprowadzenie danych przez użytkownika.

Na przykład w swoim przypadku możesz zrobić coś takiego:

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

Powyższe jest również przykładem „MRE”. Kod jest prosty i zawarty w jednej klasie, którą można kopiować / wklejać / kompilować i testować.

Przeczytaj sekcję z samouczka Swing na temat korzystania z okien dialogowych, aby uzyskać więcej przykładów użycia pliku JOptionPane.

Jeśli naprawdę chcesz używać przycisków opcji, możesz utworzyć panel z przyciskami opcji i wyświetlić je w okienku opcji przy użyciu tej showConfirmDialog(...)metody. Gdy okno dialogowe zamyka byś potem trzeba dostać polecenie od działania ButtonModelz ButtonGroup.

Zobacz: jak ustawić i zarządzać układem JOptionPane, aby zapoznać się z podstawowym przykładem tego podejścia, aby rozpocząć.