IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

AWT/Swing Java Discussion :

Erreur à l'execution d'une méthode d'un héritier de JButton


Sujet :

AWT/Swing Java

  1. #1
    Membre actif
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2016
    Messages
    149
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 23
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Février 2016
    Messages : 149
    Points : 252
    Points
    252
    Par défaut Erreur à l'execution d'une méthode d'un héritier de JButton
    Bonjour.
    Pour la programmation de mon premier jeu vidéo, j'ai décidé de créer un jeu de morpion.
    Mais un grand problème est survenue lors du premier test d'une méthode permettant
    le "changement d'étiquette" du bouton. Seulement je ne sais pas pourquoi, mais tous les
    autres boutons se figent, le bouton change bien, cependant, lorsque je passe le curseur sur les objets figés,
    voici ce qui apparaît :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
     
    Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
    	at sun.font.FontDesignMetrics.stringWidth(FontDesignMetrics.java:472)
    	at com.louloux.view.ButtonCase.paintComponent(ButtonCase.java:45)
    	at javax.swing.JComponent.paint(JComponent.java:1056)
    	at javax.swing.JComponent.paintToOffscreen(JComponent.java:5210)
    	at javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:290)
    	at javax.swing.RepaintManager.paint(RepaintManager.java:1272)
    	at javax.swing.JComponent._paintImmediately(JComponent.java:5158)
    	at javax.swing.JComponent.paintImmediately(JComponent.java:4969)
    	at javax.swing.RepaintManager$4.run(RepaintManager.java:831)
    	at javax.swing.RepaintManager$4.run(RepaintManager.java:814)
    	at java.security.AccessController.doPrivileged(Native Method)
    	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
    	at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:814)
    	at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:789)
    	at javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:738)
    	at javax.swing.RepaintManager.access$1200(RepaintManager.java:64)
    	at javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1732)
    	at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
    	at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:756)
    	at java.awt.EventQueue.access$500(EventQueue.java:97)
    	at java.awt.EventQueue$3.run(EventQueue.java:709)
    	at java.awt.EventQueue$3.run(EventQueue.java:703)
    	at java.security.AccessController.doPrivileged(Native Method)
    	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
    	at java.awt.EventQueue.dispatchEvent(EventQueue.java:726)
    	at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
    	at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
    	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
    	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
    	at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
    Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
    	at sun.font.FontDesignMetrics.stringWidth(FontDesignMetrics.java:472)
    	at com.louloux.view.ButtonCase.paintComponent(ButtonCase.java:45)
    	at javax.swing.JComponent.paint(JComponent.java:1056)
    	at javax.swing.JComponent.paintToOffscreen(JComponent.java:5210)
    	at javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:290)
    	at javax.swing.RepaintManager.paint(RepaintManager.java:1272)
    	at javax.swing.JComponent._paintImmediately(JComponent.java:5158)
    	at javax.swing.JComponent.paintImmediately(JComponent.java:4969)
    	at javax.swing.RepaintManager$4.run(RepaintManager.java:831)
    	at javax.swing.RepaintManager$4.run(RepaintManager.java:814)
    	at java.security.AccessController.doPrivileged(Native Method)
    	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
    	at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:814)
    	at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:789)
    	at javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:738)
    	at javax.swing.RepaintManager.access$1200(RepaintManager.java:64)
    	at javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1732)
    	at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
    	at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:756)
    	at java.awt.EventQueue.access$500(EventQueue.java:97)
    	at java.awt.EventQueue$3.run(EventQueue.java:709)
    	at java.awt.EventQueue$3.run(EventQueue.java:703)
    	at java.security.AccessController.doPrivileged(Native Method)
    	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
    	at java.awt.EventQueue.dispatchEvent(EventQueue.java:726)
    	at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
    	at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
    	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
    	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
    	at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
    (Vous pouvez télécharger le projet à : https://github.com/Lefou1234567/tic-tac-toe)

    Et voici le code de la méthode placée dans la classe héritière de JButton :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    public void setName(String name) {
    		this.name = name;
    	}
    Comme vous le voyez, c'est une méthode très simple et je ne vois pas du tout où est le problème...

    Code appelant la méthode :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    public void updateTable() {
    		for(int y = 0; y < valuesOfCases.length; y++) {
    			for(int x = 0; x < valuesOfCases[y].length; x++) {
    				buttonCases[y][x].setName(valuesOfCases[y][x]);
    			}
    		}
     
    	}
    Merci de l'attention !

  2. #2
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    Septembre 2009
    Messages
    12 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Points : 29 131
    Points
    29 131
    Billets dans le blog
    2
    Par défaut
    Salut,

    Une NullPointerException, c'est quand on essaye d'invoquer une méthode ou à accèder à un attribut d'une référence null. En général, il s'agit d'un accès à une variable non initialisée. Cela peut être également un retour de méthode null, une variable qu'on a affecter à null, etc.

    Déjà regarder la trace :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
    	at sun.font.FontDesignMetrics.stringWidth(FontDesignMetrics.java:472)
    	at com.louloux.view.ButtonCase.paintComponent(ButtonCase.java:45)
    On voit que le problème se situe à la ligne 45 du fichier ButtonCase.java, donc là :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    int width = fm.stringWidth(this.name);
    Forcément, la méthode stringWidth n'aime pas qu'on lui demande la largeur d'une chaine null.

    Maintenant, il faut chercher à comprendre comment this.name peut être null. Premièrement, regarder comment this.name peut être affecté :
    • par le constructeur
    • par la méthode setName

    Chercher les codes qu'on a écrit qui font new ButtonCase, ou appelle la méthode setName. A priori, aucun ne passe une chaîne null. Donc pourquoi on a cette exception, malgré tout ?.
    1. Première piste : le constructeur
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      public ButtonCase(int posX, int posY, String name) {
      		super(name);
      		this.posX = posX;
      		this.posY = posY;
      		this.name = Objects.requireNonNull(name);
      		this.addMouseListener(this);
      	}
      L'attribut name de la classe est affecté "tardivement". Il peut se passer des choses dans l'appel du super qui pourraient déclencher un appel de paintComponent. Des choses qu'on ne maîtrise pas, parce que du ressort de AWT ou Swing.

      Comment se prémunir de l'exception ? En testant le cas null justement.

      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      public void paintComponent(Graphics g) {
       
         g.setColor(currentColor);
         g.fillRect(0, 0, this.getWidth(), this.getHeight());
       
          if ( this.name!=null ) { // pas de raison de dessiner le texte name s'il n'y en a pas : du coup pas de souci de name à null
      		g.setFont(nameFont);
      		FontMetrics fm = g.getFontMetrics();
      	    int height = fm.getHeight();
      	    int width = fm.stringWidth(this.name);
       
      		g.setColor(Color.BLACK);
      		g.drawString(this.name, this.getWidth() / 2 - (width / 2), (this.getHeight() / 2) + (height / 4)); 
       
          }
       
      	}
    2. Seconde piste : la méthode setName().
      Le premier souci c'est que c'est une méthode standard de java.awt.Component. Et que AWT n'impose nullement d'avoir obligatoirement qu'un composant ait un name. Il donc possiblement des composants Swing qui donnent null comme valeur à ce paramètre, en appelant la méthode. En particulier, dans le cadre de placement dans des LayoutManager, le name et la contrainte de layout sont la même information. Comme tu as redéfinis la méthode, c'est ta méthode qui est appelée, et ta variable qui est affectée. Cela pose deux problèmes : le name du composant awt ne sera pas à jour, ou du moins avec une valeur incohérente (ce qui peut poser certains problèmes lorsque cette donnée est utilisée par AWT ou Swing) et ta variable peut être modifiée par un processus que tu ne maîtrises pas avec une valeur non voulue.

      Second souci (dans Window) :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      	public void updateTable() {
      		for(int y = 0; y < valuesOfCases.length; y++) {
      			for(int x = 0; x < valuesOfCases[y].length; x++) {
      				buttonCases[y][x].setName(valuesOfCases[y][x]);
      			}
      		}
       
      	}
      Si on remonte toute la stack d'appels, on arrive à :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      public void control() {
      		calculator.setTable(table);
      	}
      (dans GameControler)

      Appelée par
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      public void setValue(int x, int y, CaseState state) { 
      		table[y][x] = state.getValue();
      		control();
      	}
      dans AbstractContrioler

      appelée par modifyCase() déclenché par l'appui sur le bouton.

      Voilà le coupable !!!

      Donc au début, valuesOfCases est un tableau rempli de null) (private String[][] valuesOfCases = new String[3][3]; dans Window).
      Tableau qui est rempli via table de GameControler/AbstractControler, qui elle aussi est replie de null au début protected String[][] table = new String[3][3];. Donc quand tu cliques sur un bouton, tu as beau affecter la case correspondante au bouton d'une valeur (par CaseState), les autres sont toujours null, et updateTable remplit tout le tableau (donc que des cases null sauf la nouvelle cliquées), qui par la suite affecte donc les instances de ButtonCase. Un repaint la-dessus et tu as huit cases dont le name est null.
      CQFD.


      Le vrai souci je dirais, ce qui t'a empêché clairement de comprendre rapidement le problème, c'est que tu multiplies en quelque sorte la notion de modèle en ayant "36" tableaux dupliqués dans plein de classes. Ce n'est jamais bon de dupliquer des informations (on peut toujours en oublier une et se retrouver avec un état incohérent) mais surtout on s'y perd, parce qu'on met plein d'intermédiaire entre les différents éléments.
      a=null on voit qu'on met null
      a=machin.getTruc().getBidule().getCeci().getCela() beaucoup plus diffiicle de le voir !

      Tu dois avoir qu'une seule instance du modèle, et pas de duplication, ce qui te permet facilement de t'assurer que ton modèle contient bien toujours des valeurs conforment qui ne feront pas planter l'affichage. La protection contre la NPE dans paintCompoennt n'étant qu'une protection technique contre l'exception, mais pas contre l'erreur fonctionnelle (le programme qui ne fait pas ce que tu veux).

      Ensuite, le controleur doit mette à jour le modèle, pas une variable interne au controleur qu'il recopierait dans une autre classe. La vue quant à elle doit se référer au modèle pour obtenir la valeur.

      En résumé, ça veut dire que :
      1. Tu as une classe de modèle avec tableau 3x3, dans laquelle tu t'assures qu'il y a bien toujours une valeur non null et pas de moyen direct de faire autrement pour les autres classes (pas d'attributs public entre autres)
      2. Tu as un controleur qui a un attribut qui contient la référence de l'instance de modèle, et qui affecte les valeurs au modèle par les méthodes du modèle
      3. Tu as un composant (ButtonCase) qui a la référence du modèle et qui utilise le modèle pour connaitre les informations qui lui serviront à faire le rendu (éventuellement qui écoute d'ailleurs les changements dans le modèle pour se rafraichir).

      1. D'une manière générale, éviter de prendre la main sur un paramètre de AWT ou Swing. Utilise un autre nom.
        Par exemple setButtonName au lieu de setName. Les IDE affichent toujours un marqueur qui te permettra de vite voir que tu redéfinis une méthode existante (dans Eclipse, un triangle vert dans la réglette).
      2. Ensuite, protège l'affectation de ta variable contre les erreurs de programmation (le passage de null à la variable) :
        Code : Sélectionner tout - Visualiser dans une fenêtre à part
        1
        2
        3
        4
        5
        6
        public void setButtonName(String name) {
        		this.name = Objects.requireNonNull(name);
         
        	System.out.println("Bonjour !");
         
        }
        C'est par ce moyen que j'ai détecté la stack de modification qui passe null (dont je parle juste avant)
      3. Protéger contre la NPE dans le code invocatoire (voir le code de la méthode paintComponent ci-dessus
    3. S'appuyer sur le comportement normal du composant, ou faire son composant personnalisé, mais pas à partir d'un composant standard (à partir d'une JComponent, d'un JPanel éventuellement).
      Si tu t'appuies sur un JButton, au lieu de rédéfinir paintComponent, tu peux affecter un background, utiliser le texte du bouton et supprimer les décorations (peutè-être changer le border) et tu auras le comportement que tu désires nativement donc sans détournement hasardeux donc sans risque.
    4. S'arranger pour que le modèle soit sans état, ou s'il en a (des attributs qui ressemblent à un état comme tes tableaux), s'arranger pour que quelque soit l'état le composant puisse être rendu. Ne pas compter sur une intialisation lazy (un tableau rempli de null dans ton cas). Même si on pourrait s'arranger pour ne rafraichier que le bouton cliqué dans ton cas, ce qui éviterait que le refraichissement des autres pas encore cliqués causent une erreur, tu ne peux pas contrôler le dessin complet (Swing peut redessiner au besoin tout composant visible à l'écran, à ton insu).
    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

  3. #3
    Membre actif
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2016
    Messages
    149
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 23
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Février 2016
    Messages : 149
    Points : 252
    Points
    252
    Par défaut
    Merci beaucoup pour cette réponse ô combien complète !

    Je crois avoir compris l'idée et vais essayer d'améliorer tout ça demain !

  4. #4
    Membre actif
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2016
    Messages
    149
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 23
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Février 2016
    Messages : 149
    Points : 252
    Points
    252
    Par défaut
    Est-ce une bonne idée de créer une classe pour le tableau ?

  5. #5
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    Septembre 2009
    Messages
    12 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Points : 29 131
    Points
    29 131
    Billets dans le blog
    2
    Par défaut
    Si seul le modèle contient les données (et dans un MVC correctement conçu, seul le modèle contient les données), le tableau peut rester un tableau. Après si tu as des traitements particuliers que tu veux encapsuler, pourquoi pas faire une classe pour représenter la grille.
    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

  6. #6
    Membre actif
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2016
    Messages
    149
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 23
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Février 2016
    Messages : 149
    Points : 252
    Points
    252
    Par défaut
    Ca marche !

    Merci beaucoup pour l'aide; je sais pas si j'aurais trouvé le problème sans vous...
    A la prochaine !

    projet : https://github.com/Lefou1234567/tic-tac-toe

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [PostgreSQL] Connaitre erreur à l'execution d'une requête
    Par marty499 dans le forum PHP & Base de données
    Réponses: 2
    Dernier message: 14/05/2009, 10h07
  2. erreur sur execution d'une requete WD11
    Par bdo0000 dans le forum WinDev
    Réponses: 1
    Dernier message: 08/02/2009, 14h15
  3. Réponses: 3
    Dernier message: 30/08/2007, 16h23
  4. Réponses: 11
    Dernier message: 11/11/2006, 12h20
  5. Erreur d'execution d'une requete
    Par touhami dans le forum Requêtes et SQL.
    Réponses: 3
    Dernier message: 06/08/2006, 12h05

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo