проблема с подключением actionlistener к переключателю - PullRequest
0 голосов
/ 05 января 2019

Я пытаюсь создать личную викторину. Идея состоит в том, что 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 голосов
/ 05 января 2019

Сколько программистов вам нужно, чтобы поменять лампочку? 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 s, но обычно никто не заботится о нас, когда мы добавляем ActionListener, мы в основном говорим: заставляет этот ActionListener объект наблюдать мое поведение .

Итак, нам нужен кто-то, кого можно наблюдать, и наблюдатель, давайте сделаем это в вашем пользовательском интерфейсе (или в упрощенной версии): Так как вы пытаетесь использовать глобальный прослушиватель, вам нужно убедиться, что он там есть, наблюдатель (наш ActionListener) пока нулевой. Давайте создадим его экземпляр. На этот раз мы знаем, что наблюдатель не равен нулю

public class UserInterface implements runnable{
    private ActionListener observer new Observer();
    //...
    public void someMethodToCreateButtons(){
        JRadioButton observableButton = new JRadioButton("Created here");
        observableButton.addActionListener(observer);
    }

И вот, когда вы выберете observableButton, он изменит свой текст на "это работает".

Это основы, теперь я использовал эти имена observableButton и Observer по какой-то причине, ActionListeners основаны на том, что известно как observer design pattern, вы можете взять книгу о шаблонах конструкций, вы не пожалеете это.

Наконец, кажется, что вы делаете вещи слишком сложными для себя, попробуйте упростить свою логику. Возможно, сделать JPanel, содержащий различные наборы кнопок, и отображать его всякий раз, когда выполняется условие? просто плеваться здесь, но постарайся сделать это простым, надеюсь, это поможет, удачи!

0 голосов
/ 05 января 2019

Ваша главная проблема здесь:

private ActionListener listeners;

Вы создаете ActionListener, но никогда не называете его.

Вы также наследуете от ClickListener, но никогда не используете его.

public class UserInterface extends ClickListener implements Runnable

В вашем коде вы звоните:

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

Итак, вы говорите своему JRadioButton, чтобы слушатель был null.

Итак, у вас есть 2 подхода:

  1. Удалите private ActionListener listeners; из верхней части вашей программы и измените:

    b.addActionListener(listeners);
    

    Кому:

    b.addActionListener(this);
    
  2. Удалите extends ClickListener из определения вашего класса и сохраните:

    b.addActionListener(listeners);
    

    Но добавив

    listeners = new ClickListener();
    

    До

    createComponents(frame.getContentPane());
    

ИМО Я бы выбрал 2-й подход.

Кстати, чтобы ваши ActionListener действительно изменили текст, вам не нужны private переменные, а скорее получите источник и приведите его. Например:

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

Забыл упомянуть, пожалуйста, следуйте Java-соглашениям об именах , чтобы ваша программа была более читабельной и понятной для всех, кто ее читает.

  • firstWordLowerCaseVariable
  • firstWordLowerCaseMethod()
  • FirstWordUpperCaseClass
  • ALL_WORDS_UPPER_CASE_CONSTANT

И сделать правильный код для вашего кода:)

0 голосов
/ 05 января 2019

Прежде всего, графический интерфейс не должен расширять класс слушателя. Нехорошо. Лучше держать их отдельно и передавать ссылки, где это необходимо. Например, если слушателю нужна ссылка на GUI, передайте его как параметр

Кроме того, вы, очевидно, хотите сделать это немедленно ответить на выбор JRadioButton с полезным поведением. Я бы использовал ItemListener, а не ActionListener, поскольку ItemListener сообщит вам, был ли выбран переключатель. Вызов getSource() на ItemEvent даст вам текущий выбранный 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 + "]";
    }

}
0 голосов
/ 05 января 2019

Поскольку в вашем коде есть много вещей, которые нужно улучшить, я подумал, что напишу пример программы, чтобы показать вам, как это нужно делать в 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)");
    }
  }
}
...