将动作监听器连接到单选按钮时遇到麻烦

时间:2019-01-05 15:03:45

标签: java swing actionlistener

我正在尝试创建个性测验。这个想法是,一个JPanel将显示第一个问题,然后,一旦用户选择了一个单选按钮,第二个JPanel将显示第二个问题。 因为我有5个问题,每个问题有3个答案,所以我认为创建一个创建单选按钮并添加ActionListener的方法会更快,更高效,但是我很难让听众开始工作。现在,看看它是否有效,我只是想在选择按钮文本时更改它。

我尝试将侦听器添加到createButton方法中的按钮上,但是我还没有走运。最初,我在方法中将其作为参数,但是没有得到预期的结果,因此我尝试在没有将侦听器作为参数的情况下创建它。插入侦听器作为参数不会更改文本。

import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.WindowConstants;

public class UserInterface extends ClickListener implements Runnable
{
    private ActionListener listeners;
    private JFrame frame;

    public UserInterface(){
    }
@Override
public void run() {
    frame = new JFrame("title");
    frame.setPreferredSize(new Dimension(1000, 1000));

    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    createComponents(frame.getContentPane());

    frame.pack();
    frame.setVisible(true);
}
public JFrame getFrame(){
    return frame;
}
private void createComponents(Container container){

    BoxLayout layout = new BoxLayout(container, BoxLayout.Y_AXIS);
    container.setLayout(layout); 

    container.add(QuizIntro());
    container.add(QuestionOne());
    container.add(QuestionOneGroup());
}
    public JLabel QuizIntro(){
    JLabel text = new JLabel("Intro text");
    return text;
} 
public JLabel QuestionOne(){
    JLabel text = new JLabel("1. this is the first question");

    return text;
}
    public JPanel QuestionOneGroup(){
        JRadioButton int1 = createButton("This button was created with my createButton method");
        JRadioButton e1 = new JRadioButton("This button was created without that method");
        JPanel panel = new JPanel();
        panel.add(int1);
        panel.add(e1);
        return panel;
    }
    public JRadioButton createButton(String text){
        JRadioButton b = new JRadioButton(text);
        b.addActionListener(listeners);
        return b;
    }


}

这是我的动作监听器

public class ClickListener implements ActionListener {
    private UserInterface ui; 
    private JRadioButton b; 
    @Override
    public void actionPerformed(ActionEvent ae) {
        if (b.isSelected()){
            b.setText("this works");
        }
    }
}

实际结果是选择了按钮,但文本未更改。我无法弄清楚是否运行了错误的测试以查看我的听众是否正常工作,或者我的听众是否不正常工作。

4 个答案:

答案 0 :(得分:4)

您的主要问题在这里:

private ActionListener listeners;

您创建了ActionListener,但是您从未调用过它。

您还继承自ClickListener,但从未使用过它。

public class UserInterface extends ClickListener implements Runnable

在您的代码中调用:

b.addActionListener(listeners); //Listeners is null!

因此,您要告诉JRadioButton拥有一个null的侦听器。

所以,这里有两种方法:

  1. 从程序顶部删除private ActionListener listeners;并进行更改:

    b.addActionListener(listeners);
    

    收件人:

    b.addActionListener(this);
    
  2. 从您的班级定义中删除extends ClickListener并保留:

    b.addActionListener(listeners);
    

    但是添加

    listeners = new ClickListener();
    

    之前

    createComponents(frame.getContentPane());
    

IMO,我将采用第二种方法。

顺便说一句,ActionListener实际上可以更改不需要private变量的文本,而是获取源并将其强制转换。例如:

public void actionPerformed(ActionEvent ae) {
    JRadioButton b = (JRadioButton) ae.getSource();
    b.setText("this works");
}

忘记提及,请按照Java naming conventions进行操作,以使每个阅读该程序的人都可以更容易阅读和理解。

  • firstWordLowerCaseVariable
  • firstWordLowerCaseMethod()
  • FirstWordUpperCaseClass
  • ALL_WORDS_UPPER_CASE_CONSTANT

并正确缩进代码:)

答案 1 :(得分:2)

首先,GUI不应扩展侦听器类。不好。最好将它们分开,并在需要时传递引用。例如,如果侦听器需要对GUI的引用,则将其作为参数传递给

此外,您显然想做的就是以有用的行为立即响应JRadioButton选择。我将使用ItemListener而不是ActionListener,因为ItemListener会告诉您是否已选择单选按钮。在ItemEvent上调用getSource()将为您提供当前选定的JRadioButton。

例如

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;

import javax.swing.*;

@SuppressWarnings("serial")
public class UserInterface2 extends JPanel {
    public static final Object QUESTION = "question";
    // fill label with blank text to expand it
    private JLabel resultLabel = new JLabel(String.format("%150s", " "));

    // CardLayout to allow swapping of question panels
    private CardLayout cardLayout = new CardLayout();
    private JPanel centerPanel = new JPanel(cardLayout);

    public UserInterface2(List<Question> questions) {
        centerPanel.setBorder(BorderFactory.createLineBorder(Color.BLUE));
        for (Question question : questions) {
            centerPanel.add(createQPanel(question), question.getQuestion());
        }

        JPanel bottomPanel = new JPanel(new BorderLayout());
        // add button that allows swapping question panels
        bottomPanel.add(new JButton(new AbstractAction("Next") {

            @Override
            public void actionPerformed(ActionEvent e) {
                cardLayout.next(centerPanel);
            }
        }), BorderLayout.LINE_START);
        bottomPanel.add(resultLabel);

        setLayout(new BorderLayout());
        add(bottomPanel, BorderLayout.PAGE_END);
        add(centerPanel);
    }

    private JPanel createQPanel(Question question) {
        JPanel radioPanel = new JPanel(new GridLayout(0, 1));
        radioPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
        ButtonGroup buttonGroup = new ButtonGroup();
        ItemListener myItemListener = new MyItemListener(this);
        for (String answer : question.getAnswers()) {
            JRadioButton answerButton = new JRadioButton(answer);

            // this is present in case you want to extract the Question
            // object from the JRadioButton, useful for if you want to
            // test if the selected answer is correct
            answerButton.putClientProperty(QUESTION, question);

            // add our listener to the JRadioButton
            answerButton.addItemListener(myItemListener);

            // add to button group so only one can be selected
            buttonGroup.add(answerButton);

            // add to JPanel for display
            radioPanel.add(answerButton);
        }

        JPanel qPanel = new JPanel(new BorderLayout());
        qPanel.add(new JLabel(question.getQuestion()), BorderLayout.PAGE_START);
        qPanel.add(radioPanel);
        return qPanel;
    }

    // public method that the item listener will use to display selection
    public void displayResult(String selectedText) {
        resultLabel.setText(selectedText);
    }

    private static void createAndShowGui() {
        // create mock questions
        // likely this information will be in a text file
        List<Question> questions = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            String question = "Question " + i;
            List<String> answers = new ArrayList<>();
            for (int j = 0; j < 4; j++) {
                answers.add(String.format("Answer [%d %d]", i, j));
            }

            int correctIndex = (int) (Math.random() * answers.size());
            // future iteration will also need correctIndex int
            questions.add(new Question(question, answers, correctIndex));
        }

        UserInterface2 mainPanel = new UserInterface2(questions);

        JFrame frame = new JFrame("User Interface");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(mainPanel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }

}

class MyItemListener implements ItemListener {
    private UserInterface2 ui;

    public MyItemListener(UserInterface2 ui) {
        this.ui = ui;
    }

    @Override
    public void itemStateChanged(ItemEvent e) {
        JRadioButton source = (JRadioButton) e.getSource();

        String selected = "The JRadioButton " + source.getText();
        selected += e.getStateChange() == ItemEvent.SELECTED ? " has been selected"
                : " has been unselected";

        // to get the actual Question object get the client property:
        Question question = (Question) source.getClientProperty(UserInterface2.QUESTION);
        // now can get answer Strings and check if correct one selected        
        System.out.println(question);

        String correctAnswer = question.getAnswers().get(question.getCorrectIndex());;
        if (source.getText().equals(correctAnswer)) {
            selected += " and is correct";
        } else {
            selected += " and is incorrect";
        }

        // tell the GUI to display the result
        ui.displayResult(selected);
    }
}

class Question {
    private String question;
    private List<String> answers;
    private int correctIndex;

    public Question(String question, List<String> answers, int correctIndex) {
        this.question = question;
        this.answers = answers;
        this.correctIndex = correctIndex;
    }

    public String getQuestion() {
        return question;
    }

    public List<String> getAnswers() {
        return answers;
    }

    public int getCorrectIndex() {
        return correctIndex;
    }

    @Override
    public String toString() {
        return "Question [question=" + question + ", correctIndex="
                + correctIndex + "]";
    }

}

答案 2 :(得分:0)

由于您的代码中有许多需要改进的地方,我想我将编写一个示例程序来向您展示如何在Swing中完成这种事情。试试看。 (在此示例代码中,我们几乎也无法改进。但是我只是想保持简单,只解决关键点。)

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

public class Questions {

  public static void main(String[] args) {
    JFrame f = new JFrame("Questions");
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.getContentPane().add(new QuestionPanel());
    f.setBounds(300, 200, 400, 300);
    f.setVisible(true);
  }
}

class QuestionPanel extends JPanel implements ActionListener {

  private static final String ANSWER_1_TEXT = "Answer 1";
  private static final String ANSWER_2_TEXT = "Answer 2";
  private static final String ANSWER_3_TEXT = "Answer 3";

  private JRadioButton answer1;
  private JRadioButton answer2;
  private JRadioButton answer3;

  QuestionPanel() {

    answer1 = new JRadioButton(ANSWER_1_TEXT);
    answer2 = new JRadioButton(ANSWER_2_TEXT);
    answer3 = new JRadioButton(ANSWER_3_TEXT);

    answer1.addActionListener(this);
    answer2.addActionListener(this);
    answer3.addActionListener(this);

    ButtonGroup buttonGroup = new ButtonGroup();
    buttonGroup.add(answer1);
    buttonGroup.add(answer2);
    buttonGroup.add(answer3);

    setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
    add(answer1);
    add(answer2);
    add(answer3);
  }

  @Override
  public void actionPerformed(ActionEvent e) {

    JRadioButton selectedAnswer = (JRadioButton) e.getSource();
    if (selectedAnswer == answer1) {
      answer1.setText(ANSWER_1_TEXT + " (selected)");
      answer2.setText(ANSWER_2_TEXT);
      answer3.setText(ANSWER_3_TEXT);
    }
    else if (selectedAnswer == answer2) {
      answer1.setText(ANSWER_1_TEXT);
      answer2.setText(ANSWER_2_TEXT + " (selected)");
      answer3.setText(ANSWER_3_TEXT);
    }
    else if (selectedAnswer == answer3) {
      answer1.setText(ANSWER_1_TEXT);
      answer2.setText(ANSWER_2_TEXT);
      answer3.setText(ANSWER_3_TEXT + " (selected)");
    }
  }
}

答案 3 :(得分:0)

  

您需要多少个程序员来更换灯泡? 76,1进行更改,75表示他们可以做得更好。

您确实有不好的代码实践,但这通常是因为对语言设计背后的基本概念的理解不明确。因此,我不会评论您的代码对此有何不利之处,仅说明您应该了解的基本知识。

为简化起见,ActionListener是一个将对ActionPerformedEvent做出反应的对象。让我们定义一个,称为Observer的类:

观察者不知道是谁生成了该事件,所以告诉他,如果它是一个JRadioButton,让它作为一个事件使用

public class Observer implements ActionListener{
    @Override
    public void ActionPerformed(ActionEvent ae){
        Object source = ae.getSource();
        if (source instanceof JRadioButton)
            ((JRadioButton) source).setText("this works");
    }

您的JRadioButton中的任何一个都是不断生成ActionEvent的对象,但是通常我们不在乎的对象是观察对象,当我们添加ActionListener时,我们基本上是在说:使这个{{1 }}对象观察我的行为。

所以我们需要一个被观察者和一个观察者,让他们在您的UI(或其简化版本)中进行制作: 由于您尝试使用全局侦听器,因此需要确保那里没有侦听器,因此观察者(我们的ActionListener)目前为null。让我们实例化它这次,我们知道观察者不为空

ActionListener

就这样,当您选择public class UserInterface implements runnable{ private ActionListener observer new Observer(); //... public void someMethodToCreateButtons(){ JRadioButton observableButton = new JRadioButton("Created here"); observableButton.addActionListener(observer); } 时,它将文本更改为“ this works”。

这些都是基础知识,现在,我使用这些名称observableButtonobservableButton是有原因的,ActionListeners基于Observer,您可以选择一本有关设计模式,您不会后悔。

最后,看来您使自己变得太困难了,请尝试简化逻辑。也许制作一个observer design pattern包含不同按钮集并在条件为真时显示它?只是在这里吐痰,但请尽量保持简单,希望对您有帮助,祝您好运!