メソッド呼び出し後にクラス変数が更新されない

Dec 01 2020

私はJavaにまったく慣れておらず、調査してグーグルで多くの回答を読んだ後でのみ、これを投稿しています。私はちょっと迷っています。少しのガイダンスが大いに役立ちます。以下は、「ActionListener」インターフェースを実装するクラスのメソッドです。私がやろうとしているのはこれです:1回クリックすると2つのラジオボタンの形で2つのオプションがある新しいウィンドウが開くはずのボタンがあります。コードでさらに使用するために選択されたラジオボタンを知る必要があります。「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」メソッドの宣言は1つだけですか、それともオーバーロードできますか?
  3. 「actionPerformed」メソッドから戻り値を取得することは可能ですか?

少しでもヒントをいただければ幸いです。私は本当にたくさん試しましたが、それをここに投稿しました。事前にどうもありがとうございました。

小さな編集:そこにある「actioncommand」自体を「System.out.println」すると、コンソールで印刷して完全に機能します。しかし、クラス変数を更新して、メソッド呼び出しの後にそれを出力しようとしたときではありません。これが役立つなら、Dunno。

回答

1 HovercraftFullOfEels Dec 01 2020 at 10:30

JFrameはモーダルではありません。JFrameを作成して表示すると、コードフローがブロックされないため、JFrameが表示されているとき、ユーザーが変更する前に、scoreOptionの値を抽出します。モーダルダイアログとして作成された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を表示すると、ダイアログが処理される直前にテストテキストがコンソール表示されます。ボタンを押してダイアログを表示すると、ボタンを押してテキストを変更するまで、テストテキストの表示が遅れます。

フレームボタンを2回押すと、テキストは最初に表示されたときに設定されているため、最終的に正しいテキストが表示されます。

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

上記も「MRE」の一例です。コードは単純で、コピー/貼り付け/コンパイルとテストが可能な単一のクラスに含まれています。

の使用例については、Swingチュートリアルの「ダイアログの使用方法」のセクションをお読みくださいJOptionPane

本当にラジオボタンを使用したい場合は、ラジオボタンを使用してパネルを作成し、showConfirmDialog(...)メソッドを使用してオプションペインに表示できます。ダイアログが閉じたら、ButtonModelのからアクションコマンドを取得する必要がありますButtonGroup

このアプローチの基本的な例については、JOptionPaneのレイアウトを設定および管理する方法を参照してください。