7
votes

JButton reste pressé lorsque la mise au point a été volée par JOPTIONPANE

Je suis nouveau à balancer et j'ai une situation. Je concevons une application qui rend les composantes de l'interface graphique de manière dynamique sur une entrée de fichier XML (métadonnées). Maintenant, la plupart de mes jtextfields ont mis l'introduction à leur introduction, à des fins de validation. Le vérificateur d'entrée apparaît Joptionpane chaque fois qu'il y a une entrée non valide.

Maintenant, si un utilisateur entrait une donnée non valide et passe à l'avance, puis clique sur un bouton sur le panneau, une boîte de dialogue apparaît et l'utilisateur doit y répondre. Mais après cela, le bouton ne peint pas pour libérer l'état. Il semblait toujours que cela soit pressé mais en fait ce n'est pas le cas. Comme l'ensemble du code est assez désordonné, je pose le scénario de problème dans le code ci-dessous: -

Que dois-je faire pour que le jbutton ait l'air non pressé? J'apprécierais que la logique soit également expliquée.

Merci d'avance. xxx


6 commentaires

S'il vous plaît, essayez d'envelopper l'appel de ShowMessageDialog dans une courante et donnez-la aux oscillabilités :: invoqater (runnable)


@ GD14 Bonjour, j'ai essayé l'approche que vous avez énoncée, mais cela ne semble pas fonctionner. Le code modifié est le suivant: - Message de chaîne finale = "Valeur illégale:" + tf.getText (); javax.swing.swingutities.invokelater (nouveau runnable () {Public Void Run () {JOPTIONPANE.SHOWMESSAGEDIALEG (NULL, MESSAGE, VALEUR IBLALGAL ", JOPTIONPANE.ERROR_MESSAGE);}}); retourner faux;


Je vois. Quelle est votre version OS et Java? Je suis sur OSX avec Java 1.6 et ça marche très bien.


@ GD1 OS - Windows 7 64 Bit et Java - 1.6.27. J'ai trouvé une solution que je détaille ci-dessous, s'il vous plaît laissez-moi savoir s'il s'agit d'une bonne solution. Merci


Vous voudrez peut-être consulter Stackoverflow.com/Questtions/12165355/... Pour plus de discussion sur la manipulation des erreurs dans une application Swing.


@ GD1 Bonne idée - mais n'aidez pas ici: même avec l'effet secondaire déplacé dans la méthode de vérificateur correcte (devoiryyeldfocus au lieu de vérifier) ​​- c'est un bug :-)


5 Réponses :


3
votes

La méthode vérifier n'est en réalité pas un bon endroit pour ouvrir un JOPTIONPANE.

Il existe plusieurs approches que vous pourriez envisager de résoudre votre problème:

  1. Vous voulez que ce JOPTIONPANE apparaisse chaque fois que le champ de texte souligne l'accent et que l'entrée est incorrecte: utilisez un focuslistener sur le champ JTextfield et agissez sur les événements appropriés
  2. Vous voulez que ce joptionpane apparaisse chaque fois que les boutons soient enfoncés: utilisez votre actionListener pour le faire si l'entrée est incorrecte.

    Voici un petit extrait de cette dernière option: xxx

    Veuillez également envisager de définir le fonctionnement de fermeture par défaut du jframe au lieu d'ajouter un auditeur de fenêtre (mais C'est une bonne approche pour utiliser un WindowListener si vous souhaitez apparaître une boîte de dialogue demandant à l'utilisateur s'il est sûr qu'il souhaite quitter votre application).


6 commentaires

Merci de garder cela à l'esprit et de faire des changements nécessaires. En attendant, j'ai trouvé une solution que je détaille ci-dessous. Pourriez-vous s'il vous plaît confirmer si c'est une bonne solution. Merci


Merci cette solution fonctionne parfaitement. Juste une autre question, je voudrais basculer des panneaux (déterminé et créé de manière dynamique) sur ma mise en page basée sur le bouton Cliquez sur le bouton (appelé Suivant). Dans ce cas, comment devrais-je aller de l'avant? Merci


@dareurdream Je n'ai pas vu votre carte de carte, mais l'idée de base consiste à appeler ensuite () et précédente () sur le CardLayout. Si vous souhaitez que un composant spécifique apparaisse, ajoutez-le au conteneur avec une contrainte de chaîne et utilisez le spectacle de méthode () avec la chaîne correspondant au composant souhaité. En voir plus ici


oops, mélangé les solutions: votre introuvable était toujours valide :-) Néanmoins, ce n'est pas correct: l'effet secondaire appartient à son devoir, pas dans un gestionnaire externe (vous ne voulez pas vraiment l'ajouter à tous les boutons le potentiellement besoin de s'appuyer sur un composant valide, faites-vous :-)


@ Kleopatra Je ne suis pas sûr de comprendre clairement ce que vous dites et si vous vous référez (ou non) à d'autres commentaires que j'ai postés. Quoi qu'il en soit, la mise en place de l'effet JOPTIONPANE dans le doutyeldfocus ne me semble pas indûment: si, par exemple, vous avez un champ de texte avec Inconversion et un bouton OK et Annuler, le JOPTIONPANE ne doit apparaître que lorsque vous appuyez sur OK et non lorsque vous appuyez sur ANNULER.


C'est ce que la propriété VerifyInPutWhenfocustarget est pour: c'est vrai par défaut (approprié pour le bouton OK) et peut être défini sur FALSE pour un bouton Annuler (qui ne validait pas du tout). Le dactylierfocus est conçu pour contenir tous les effets secondaires.



1
votes

J'ai ajouté un appel à oscillabilité code> pour vous assurer que l'interface graphique est sur le thread de l'événement et j'ai supprimé votre référence à la trame.

L'interface graphique fonctionne pour moi sur Windows XP. P >

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.InputVerifier;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

public class VerifierTest implements Runnable {

    private static final long serialVersionUID = 1L;

    public VerifierTest() {

    }

    @Override
    public void run() {
        JFrame frame = new JFrame();
        frame.setSize(400, 200);

        JTextField tf;
        tf = new JTextField("TextField1");
        tf.setInputVerifier(new PassVerifier());
        frame.getContentPane().add(tf, BorderLayout.NORTH);

        final JButton b = new JButton("Button");
        b.setVerifyInputWhenFocusTarget(true);
        frame.getContentPane().add(b, BorderLayout.EAST);
        b.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (b.hasFocus())
                    System.out.println("Button clicked");
            }
        });

        frame.addWindowListener(new MyWAdapter());
        frame.setVisible(true);
    }

    public static void main(String[] args) {
       SwingUtilities.invokeLater(new VerifierTest());
    }

    class MyWAdapter extends WindowAdapter {
        @Override
        public void windowClosing(WindowEvent event) {
            System.exit(0);
        }
    }

    class PassVerifier extends InputVerifier {
        @Override
        public boolean verify(JComponent input) {
            JTextField tf = (JTextField) input;
            String pass = tf.getText();
            if (pass.equals("Manish"))
                return true;
            else {
                String message = "illegal value: " + tf.getText();
                JOptionPane.showMessageDialog(tf.getParent(), message,
                        "Illegal Value", JOptionPane.ERROR_MESSAGE);

                return false;
            }
        }
    }
}


2 commentaires

L'interface graphique travaille aussi pour moi, mais le vrai problème est avec le look du bouton. Si nous observons la console, nous trouverons que le bouton de sortie SYS OUT "cliqué sur" n'est jamais affiché. Cela signifie que le bouton n'est jamais cliqué. Mais toujours le bouton semble être pressé ... de toute façon j'ai trouvé une solution et j'aimerais que votre opinion sur ça. Merci.


La mise en œuvre de l'intrigeur est invalide



1
votes

J'ai ajouté un nouvel auditeur de souris sur le bouton comme ci-dessous et il semble fonctionner bien pour moi maintenant, mais je ne suis pas sûr que si c'est un bon moyen de rectifier l'état de sélection des boutons.

package test;

import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.InputVerifier;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.plaf.basic.BasicButtonListener;

public class VerifierTest extends JFrame {

    private static final long serialVersionUID = 1L;

    public VerifierTest() {
        JTextField tf;
        tf = new JTextField("TextField1");

        getContentPane().add(tf, BorderLayout.NORTH);
        tf.setInputVerifier(new PassVerifier());

        final JButton b = new JButton("Button");
        b.setVerifyInputWhenFocusTarget(true);
        getContentPane().add(b, BorderLayout.EAST);
        b.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (b.hasFocus())
                    System.out.println("Button clicked");
            }
        });

        b.addMouseListener(new BasicButtonListener(b) {
            @Override
            public void mouseExited(MouseEvent e) {
                ((JButton)e.getSource()).getModel().setArmed(false);
                ((JButton)e.getSource()).getModel().setPressed(false);
            }

        });

        addWindowListener(new MyWAdapter());
    }

    public static void main(String[] args) {
        Frame frame = new VerifierTest();
        frame.setSize(400, 200);
        frame.setVisible(true);
        // frame.pack();
    }

    class MyWAdapter extends WindowAdapter {

        public void windowClosing(WindowEvent event) {
            System.exit(0);
        }
    }

    class PassVerifier extends InputVerifier {

        public boolean verify(JComponent input) {
            JTextField tf = (JTextField) input;
            String pass = tf.getText();
            if (pass.equals("Manish"))
                return true;
            else {
                final String message = "illegal value: " + tf.getText();
                        JOptionPane.showMessageDialog(null, message,
                                "Illegal Value", JOptionPane.ERROR_MESSAGE);

                return false;
            }
        }
    }
}


3 commentaires

Bien que cette solution puisse fonctionner, vous enfreignez réellement le CONTRAT INPUTVERIFICATEUR : Cette méthode ne devrait avoir aucun effet secondaire de sorte que tout ce qu'il devrait faire est de vérifier l'entrée du composant et de revenir true ou false. De plus, vous ne devriez pas avoir à "manuellement" modifier l'état du bouton (à moins que ce soit vraiment un effet souhaité)


@GuillaumaMePolet - J'accepte tout mot que vous avez dit et je modifierais ma solution à la solution suggérée par vous ci-dessus. J'aimerais connaître vos pensées sur celui-ci aussi- Stackoverflow .com / questions / 12541879 / ...


+ 1 - sucer manuellement le bouton hors de son état bizarre de buggy est la seule chose qui peut être faite ici, Mouselistener va bien si cela fonctionne



1
votes

Premier: tous les implémentations de l'introuveur qui ouvrent la boîte de dialogue en vérification () sont invalides . Ils ont violé leur contrat, API DOC:

Cette méthode ne devrait avoir aucun effet secondaire.

avec le "devrait" vraiment dire "ne doit pas" ". Le bon endroit pour les effets secondaires est doutyyeldfocus.

second: déplacer l'effet secondaire (affichant la boîte de dialogue du message) correctement dans le doutonyeldfocus ne fonctionne pas aussi bien ... en raison d'un bug (ils appellent la demande de fonctionnalité informatique ;-) , c'est plus âgé d'une décennie et dans le top 10 rfes

être un bug-autour d'un bug, @ Le mouselistener de Dareurdrem est aussi bon que n'importe quel hack ouvrier peut obtenir: -)

update

Après avoir joué un peu avec des options différentes pour pirater le bogue, voici Un autre hack - c'est aussi fragile que tous les hacks sont (et ne survivent pas à une bascule LAF, doit être réinstallé si une mise bas dynamique est requise)

pour pirater le comportement de la souris L'approche de base est de crochet dans l'auditeur installé par l'interface utilisateur:

  • trouver l'original
  • Mettre en place un auditeur personnalisé qui déléguette la plupart des événements directement à l'original
  • Pour les événements pressés Demander la mise au point d'abord: si vous avez donné des délégués à Original, sinon ne faites rien

    La dernière balle est légèrement plus impliquée car les événements de mise au point peuvent être asynchrones, nous devons donc invoquer le chèque d'être concentré. Invoquant, à son tour, nécessite d'envoyer une libération au cas où personne ne s'est opposé.

    Une autre quirk est l'action pressée du rootpane (pour sa valeur par défautbutton): elle est faite sans respecter les entrées d'introduction en appelant inconditionnellement DocLick. Qui peut être piraté en raccordant dans l'action, suivant le même schéma que l'accrochage dans le mouselistener:

    • Trouvez l'action pressée du rootpane
    • Implémentez une action personnalisée qui vérifie un introducteur potentiellement vetoing: délégué à l'original sinon, ne faites rien sinon

      L'exemple modifié le long de ces lignes: xxx


0 commentaires

0
votes

En réalité, le problème réel est de savoir comment le système de focalisation et les auditeurs AWT interagissent. Il y a quelques insectes déclarées en Java que les développeurs se rendent en arrière sur qui est responsable. L'auditeur de souris fait: Processmouseevent et dans cette logique, il est demandé à la logique actuelle de donner la mise au point. il échoue. Mais parce que la moitié de l'événement est déjà traitée, le bouton devient armé et la mise au point reste avec le champ.

J'ai enfin vu un commentaire de développeur: Ne laissez pas l'auditeur de continuer si le champ n'est pas autorisé à perdre la mise au point. p>

Par exemple: Définissez un jtextfield avec des modifications pour permettre uniquement les valeurs

protected void processMouseEvent(MouseEvent e) {
    if ( e.getComponent() != null && e.getComponent().isEnabled() ) { //should not be processing mouse events if it's disabled.
            if (e.getID() == MouseEvent.MOUSE_RELEASED && e.getClickCount() == 1) {
                // The mouse button is being released as per normal, and it's the first click. Process it as per normal.
                super.processMouseEvent(e);

                // If the release occured within the bounds of this component, we want to simulate a click as well
                if (this.contains(e.getX(), e.getY())) {
                    super.processMouseEvent(new MouseEvent(e.getComponent(),
                                                            MouseEvent.MOUSE_CLICKED,
                                                            e.getWhen(),
                                                            e.getModifiers(),
                                                            e.getX(),
                                                            e.getY(),
                                                            e.getClickCount(),
                                                            e.isPopupTrigger(),
                                                            e.getButton()));
                }
            }
            else if (e.getID() == MouseEvent.MOUSE_CLICKED && e.getClickCount() == 1) {
                // Normal clicks are ignored to prevent duplicate events from normal, non-moved events
            }
            else if (e.getID() == MouseEvent.MOUSE_PRESSED && e.getComponent() != null && (e.getComponent().isFocusOwner() || e.getComponent().requestFocusInWindow())) {// if already focus owner process mouse event
                super.processMouseEvent(e); 
            }
            else {
                // Otherwise, just process as per normal.
                if (e.getID() != MouseEvent.MOUSE_PRESSED) {
                    super.processMouseEvent(e); 
                }
            }
        }
}


0 commentaires