2
votes

difficulté à connecter un écouteur d'action à un bouton radio

J'essaye de créer un quiz de personnalité. L'idée est qu'un JPanel apparaîtra avec la première question, puis une fois que l'utilisateur sélectionne l'un des boutons radio, le second JPanel avec la deuxième question apparaîtra. Comme j'ai 5 questions chacune avec 3 choix de réponses, j'ai pensé qu'il serait plus rapide et plus efficace de créer une méthode qui crée des boutons radio et ajoute un ActionListener mais j'ai du mal à faire travailler l'auditeur. En ce moment, pour voir si cela fonctionne, j'essaie simplement de changer le texte du bouton lorsqu'il est sélectionné.

J'ai essayé d'ajouter l'auditeur au bouton dans la méthode createButton mais je n'ai pas eu de chance. À l'origine, je l'avais comme paramètre dans la méthode mais cela n'a pas obtenu le résultat attendu, j'ai donc essayé de le créer sans l'écouteur comme paramètre. L'insertion de l'écouteur en tant que paramètre n'a pas changé le texte.

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

voici mon écouteur d'action

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


}

Le résultat réel est que le bouton est sélectionné mais le texte ne change pas. J'ai du mal à déterminer si j'exécute le mauvais test pour voir si mon auditeur fonctionne ou si mon auditeur ne fonctionne tout simplement pas.


25 commentaires

Il y a beaucoup de problèmes avec ce code, y compris un héritage inapproprié, en donnant à votre classe d'écouteur des variables privées, des variables qui restent nulles et inutiles, et en ajoutant des variables nulles en tant qu'écouteur à un objet. Il est franchement difficile de savoir quel est votre problème principal. Veuillez envisager de créer un exemple reproductible minimal valide et indiquant / montrant clairement le problème. Si c'était mon code, je recommencerais TBH.


Aussi: le code se compile-t-il? Si oui, comment cela fonctionne-t-il bien? Et comment fonctionne-t-il mal? Voyez-vous des exceptions? Voyez-vous une NullPointerException, peut-être? Veuillez consulter la Comment demander pour en savoir plus sur la manière de poser des questions plus faciles à comprendre et à répondre.


@HovercraftFullOfEels si une variable nulle et inutile, vous voulez dire celle de l'interface utilisateur dans le programme d'écoute d'action qui est une erreur d'une tentative précédente que j'ai oublié de commenter. Le code se compile sans exception. Les boutons radio s'affichent tous. J'essaie simplement de sélectionner le premier bouton pour voir si le texte du bouton change le texte. J'ai essayé un tas de variantes avec mon auditeur sur le bouton et je suis sûr qu'il reste du code d'une tentative infructueuse. qu'entendez-vous par l'ajout d'une variable nulle en tant qu'écouteur auquel appartient un objet?


@HovercraftFullOfEels mon principal problème est simplement d'essayer de déterminer si l'auditeur est correctement connecté au bouton.


Dans votre classe ClickListener, c'est une variable inutile: private JRadioButton b; comme l'est private UserInterface ui; . Où attribuez-vous un objet viable à l'une ou l'autre variable? Comment est-ce possible? Et encore une fois, votre code lève-t-il des exceptions lorsqu'il est exécuté, lorsque les JRadioButtons sont enfoncés? Et encore une fois, veuillez publier un meilleur code, du code que nous pouvons compiler, exécuter et tester, un exemple reproductible minimal . Je vous exhorte à nouveau à lire ce lien important.


En ce qui concerne votre question directe, je suppose que non, ce n'est pas le cas, mais encore une fois, nous ne voyons pas de code où vous créez même un objet ActionListener car votre code est si incomplet.


@HovercraftFullOfEels aucune exception n'est levée. rien ne se passe lorsque le bouton radio est sélectionné autre que le bouton de remplissage. Je lis ce lien maintenant. Je vais modifier mon code pour inclure mon constructeur UserInterface où j'initialisais mon auditeur


Encore une fois, vous voudrez ajouter plus que juste le constructeur - vous voudrez publier un exemple reproductible minimal valide , une suggestion que j'ai déjà faite ci-dessus. Je suis sur ce site Web depuis plus de 5 ans et mon expérience avec ce type de questions a montré qu’un MCVE est votre meilleur ticket pour obtenir une réponse rapide, décente et complète. Encore une fois, veuillez lire ce lien important.


@HovercraftFullOfEels regarde je suis un débutant. et je l'ai lu. et je pensais avoir inclus tout le code pertinent mais je suppose que je ne l'ai pas fait. pouvez-vous s'il vous plaît développer ce que vous devez voir? Je peux inclure ma méthode createComponent si vous voulez que les boutons soient ajoutés avec succès.


Comprenez que nous ne demandons pas le programme entier, nous voulons un programme de représentation compilable et exécutable, qui démontre le problème. C'est un nouveau programme, que nous pouvons copier et coller dans nos IDE (j'utilise Eclipse, mais ce n'est pas pertinent). Vous demandez l'aide gratuite de bénévoles, et cela vous aidera énormément si vous pouvez rendre aussi facile que possible la compréhension de votre code et de votre problème, et pourquoi le code ne fonctionne pas.


Par exemple, ceci, public UserInterface () {this.listeners = listeners; } est absurde car vous définissez simplement le champ des écouteurs (qui est nul) égal à lui-même puisque le constructeur ne prend aucun paramètre.


@HovercraftFullOfEels me donne quelques secondes pour ajouter du code plus pertinent. en attendant, dois-je me débarrasser de tout le code que j'ai commenté? J'ai pensé que les tentatives infructueuses commentées pourraient aider.


Oui, rendez votre code aussi propre et facile à lire que possible. Je me débarrasserais du code commenté. Si important, montrez-le dans un autre bloc de code, tel que "J'ai aussi essayé ceci ... mais cela n'a pas fonctionné à cause de ..."


@HovercraftFullOfEels vous avez mentionné plus tôt l'héritage inapproprié. aussi je crois que dans une de mes tentatives ratées hier j'avais des paramètres dans la méthode du constructeur de mon auditeur. vous dites que je devrais refaire le constructeur de mon auditeur avec le bouton radio comme paramètre?


Ce que @HovercraftFullOfEels essaie de dire avec exemple minimal reproductible est: 1) Créer un nouveau programme à partir de zéro qui ne contient que le code minimum pour afficher votre problème mais qui est suffisamment complet pour que nous puissions le copier-coller sur nos IDE et voir le même problème que vous. Voici un exemple que j'ai préparé pour une réponse hier. Si vous copiez-collez le code, vous devriez voir la même sortie que j'ai publiée dans les captures d'écran sans rien faire d'autre que de simplement le copier-coller. C'est ce que nous attendons de vous


Voici un autre exemple d'utilisation de JRadioButtons et avec l'héritage, c'est aussi une réponse de l'aéroglisseur et est un peu similaire à ce que vous voulez faire.


@HovercraftFullOfEels J'ai ajouté tous mes composants et tout. vous devriez pouvoir l'exécuter en le copiant et en le collant dans eclipse


J'apprécie l'effort mais toujours, pas encore de méthode principale, ce public UserInterface () {this.listeners = listeners; } est un code sans valeur (je suppose que vous voulez utiliser un paramètre avec ce constructeur ??), nous ne voyons pas où vous créez ou ajoutez vos ActionListeners ....


.............Persévère


De plus, vous utilisez des variables qui ne sont pas déclarées dans le code ci-dessus, telles que frame , vous avez des méthodes déclarées pour renvoyer des objets, qui ne renvoient rien, comme QuestionOneGroup () (et devrait être renommé questionOneGroup () pour se conformer aux conventions de dénomination), .... nous voulons du code réel , pas de type, de sorte code. Oui, je demande beaucoup, mais encore une fois, nous sommes des bénévoles et vous demandez une aide gratuite au débogage, donc je n'en demande pas trop.


@HovercraftFullOfEels J'ai ajouté toutes les importations. Je l'ai copié et collé dans mon IDE et il a été compilé. faites-moi savoir s'il y a autre chose.


se rapprocher, mais vous avez toujours ce code sans valeur: public UserInterface () {this.listeners = listeners; } vous voyez que ça ne sert à rien, non?


@HovecraftFullOfEels ouais, j'ai réalisé que je n'avais pas copié et collé toutes les variables. tout devrait être là. J'ai supprimé l'auditeur inutile dans le constructeur.


Merci de vous en tenir à cela et d'améliorer la question.


Oui! Je suis d'accord avec Hovercraft et j'ai voté pour les efforts déployés pour créer un MCVE.


4 Réponses :


0
votes

Comme il y a beaucoup de choses à améliorer dans votre code, j'ai pensé que j'écrirais un exemple de programme pour vous montrer comment faire ce genre de chose dans Swing. Essayez-le et voyez. (Il y a aussi peu de choses que nous pouvons améliorer dans cet exemple de code. Mais je voulais juste garder les choses simples et ne traiter que les points clés.)

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


0 commentaires

4
votes

Votre principal problème est ici:

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

Vous créez un ActionListener mais vous ne l'appelez jamais.

Vous héritez également de ClickListener mais ne l'utilisez jamais.

createComponents(frame.getContentPane());

Dans votre code, vous appelez:

listeners = new ClickListener();

Donc , vous dites à votre JRadioButton d'avoir un auditeur qui est null.

Donc, ici vous avez 2 approches:

  1. Supprimez les écouteurs privés ActionListener; du haut de votre programme et modifiez:

    b.addActionListener(listeners);
    

    À:

    b.addActionListener(this);
    
  2. Supprimez étend ClickListener de votre définition de classe et conservez:

    b.addActionListener(listeners);
    

    Mais en ajoutant

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

    Avant

    public class UserInterface extends ClickListener implements Runnable
    

IMO Je prendrais la deuxième approche.

BTW pour votre ActionListener pour changer réellement le texte dont vous n'avez pas besoin private code > variables mais récupérez plutôt la source et lancez-la. Par exemple:

private ActionListener listeners;

Vous avez oublié de mentionner, veuillez suivre Conventions de dénomination Java afin que votre programme soit plus lisible et compréhensible par tous ceux qui le lisent.

  • firstWordLowerCaseVariable
  • firstWordLowerCaseMethod()
  • FirstWordUpperCaseClass
  • ALL_WORDS_UPPER_CASE_CONSTANT

Et indentez correctement votre code également :)


4 commentaires

très belle réponse 1+


Eh bien, le vôtre est à peu près meilleur que le mien @HovercraftFullOfEels J'ai également ajouté un 1+ au vôtre


Non, le vôtre explique mieux les problèmes et le PO ferait bien de sélectionner celui-ci comme la réponse


Je suis d'accord avec cela, mais je pense que les deux réponses se complètent au fur et à mesure que vous allez plus loin dans l'ajout d'un modèle MVC pour ce que l'utilisateur doit faire ensuite, en supprimant l'héritage, etc.



2
votes

Tout d'abord, l'interface graphique ne doit pas étendre la classe d'écouteur. Pas bon. Mieux vaut les garder séparés et passer des références si nécessaire. Par exemple, si l'auditeur a besoin d'une référence à l'interface graphique, transmettez-la en tant que paramètre

De plus, vous voulez apparemment faire est de répondre immédiatement à la sélection JRadioButton avec un comportement utile. J'utiliserais un ItemListener et non un ActionListener, car ItemListener vous dira si le bouton radio a été sélectionné. L'appel de getSource () sur ItemEvent vous donnera le JRadioButton actuel qui a été sélectionné.

par exemple,

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 + "]";
    }

}

p>

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

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

}


0 commentaires

0
votes

De combien de programmeurs avez-vous besoin pour changer une ampoule? 76, 1 pour le changer et 75 pour dire comment ils auraient pu mieux faire.

Vous avez de mauvaises pratiques de code, mais c'est généralement à cause d'une compréhension fragile des concepts fondamentaux derrière la conception du langage. Je ne commenterai donc pas en quoi votre code est mauvais pour ceci ou cela , expliquez simplement les bases que vous devez connaître.

Pour simplifier, un ActionListener code > est un objet qui réagira sur un ActionPerformedEvent . Définissons-en une, une classe appelée Observer:

Observer n'a aucune idée de qui a généré l'événement, alors dis-lui, et si c'est un JRadioButton, utilisons-le comme un

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

Tous vos JRadioButton sont des objets qui génèrent constamment des ActionEvent , mais personne qui nous tient à cœur ne regarde habituellement, lorsque nous ajoutons un ActionListener nous disons en gros: faites que cet objet ActionListener observe mon comportement.

Nous avons donc besoin de quelqu'un à observer et d'un observateur, laisse faites-les dans votre interface utilisateur (ou une version simplifiée de celle-ci): Puisque vous essayez d'utiliser un écouteur global, vous devez vous assurer qu'il y en a un, observer (notre ActionListener) est nul pour le moment. Permet de l'instancier Cette fois, nous savons que l'observateur n'est pas nul

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

Et voilà, quand vous sélectionnez observableButton , il sera changez son texte en "ça marche".

Ce sont les bases, maintenant, j'ai utilisé ces noms observableButton et Observer pour une raison, ActionListeners sont basé sur ce que l'on appelle le modèle de conception d'observateur , vous pourriez prendre un livre sur les modèles de conception, vous ne le regretterez pas.

Enfin, il semble que vous rendez les choses trop difficiles pour vous-même, essayez de simplifier votre logique. Peut-être créer un JPanel contenant différents ensembles de boutons et l'afficher chaque fois qu'une condition est vraie? crache juste ici, mais essayez de rester simple, espérons que cela vous aidera, bonne chance!


0 commentaires