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

 Java Discussion :

Bouger image à l'aide d'un bouton


Sujet :

Java

  1. #1
    Candidat au Club
    Femme Profil pro
    Étudiant
    Inscrit en
    décembre 2019
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 21
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : décembre 2019
    Messages : 6
    Points : 4
    Points
    4
    Par défaut Bouger image à l'aide d'un bouton
    Bonjour/Bonsoir,
    je viens vers vous pour vous demander votre ide sur un problème que je ne comprend pas du tout.Cela fait 2j que j'essaie de faire se déplacer,une image lorsque je clic sur un bouton. Apres beaucoup d'essaie infructueux je décide d'afficher mes variables pour voir d'ou viens le problème, et là magie... cela fonctionne, mon image se déplace bien lorsque j'appuie sur le bouton... Croyant le problème résolue par magie je décide de supprimer les lignes qui font affiche mes variables et l'a le problème réapparaît. Je ne comprend donc pas.. puisque ces lignes n'ont aucun impacte dans le déplacement de l'image

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
           Bouton but= new Bouton("Jesuisla", false);
           pan.add(but);
           but.posBouton(but, 800,200);
     int i=0;
    while(true) {     boolean prevapp=but.app;
         System.out.println("prevapp="+prevapp);
    	 if (but.isApp()!=prevapp){
    		 i=i+1;
    		 prevapp=but.isApp();
    		 but.setApp(false);
      	   ((Panneau) pan).posJoyaux(i,2);
      	   pan.repaint();
     }
     }
    Voila le bout de code posant probleme. Lorsque la ligne "
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    System.out.println("prevapp="+prevapp);
    n'est plus là, l'image ne se déplace pas. ( la méthode permettant de se faire déplacer l'image est posJoyaux(x,y))

    Mercii !

  2. #2
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    septembre 2009
    Messages
    12 373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    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 373
    Points : 29 307
    Points
    29 307
    Billets dans le blog
    2
    Par défaut
    Salut,

    Sans plus de code, il est difficile de te répondre. Il n'y a même pas une seule classe standard connue. C'est du quoi comme UI ? du Swing ? du JavaFX ?

    Je soupçonne un problème de famine avec ton while(true) : le System.out.println() libérerait juste assez de temps pour que le thread graphique se réveille assez pour tourner (et donc permettre de réagir à l'appui du bouton). Vois-tu le bouton s'enfoncer ?

    La méthode .isApp() sert-elle à tester que le bouton est enfoncé ?

    On ne procède pas de cette manière ni en Swing, ni en JavaFX. On évite même dans la plupart des cas de faire un while(true) sans mettre un système de wait/notify ou autre système d'attente, pour éviter de consommer du CPU continuellement. Et on procède en particulier par événement dans une interface graphique, pas en testant continuellement un état.
    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
    Candidat au Club
    Femme Profil pro
    Étudiant
    Inscrit en
    décembre 2019
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 21
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : décembre 2019
    Messages : 6
    Points : 4
    Points
    4
    Par défaut
    Bonjour,

    J'imagine bien que ce n'est pas la bonne façon de faire mais je n'ai pas trouvé comment faire après plusieurs expérimentations...

    Voila mes 3 classes fenêtre panneau et bouton :

    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
    package Robot;
    import java.awt.Color;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
     
    public class Fenetre extends JFrame{
     
    	public static boolean begin;
     
    	public Fenetre() {
     
    		this.setTitle("Robot Turtle");
    		this.setSize(1200,800);
    		this.setLocationRelativeTo(null);
    		this.setResizable(false);
    		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		////////////////////////////////////////
    		////////////////////////////////////////
     
    		JPanel pan = new Panneau();
    		pan.setBackground(Color.blue);
    		this.setContentPane(pan);
    		pan.setLayout(null);
    		this.setVisible(true);
     
     
     
    		/////////////////////////////////////////
    		/////////////////////////////////////////
           Bouton but= new Bouton("Jesuisla", false);
           pan.add(but);
           but.posBouton(but, 800,200);
     int i=0;
    while(true) {     boolean prevapp=but.app;
         System.out.println("prevapp="+prevapp);
    	 if (but.isApp()!=prevapp){
    		 i=i+1;
    		 prevapp=but.isApp();
    		 but.setApp(false);
      	   ((Panneau) pan).posJoyaux(i,2);
      	   ((Panneau) pan).remplacerImage(null, "images/turtle4.png");
      	   pan.repaint();
     }
     }
     
     
           }
    	}
    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
    package Robot;
    import java.awt.Color;
    import java.awt.Graphics;
    import java.awt.Image;
    import java.io.File;
    import java.io.IOException;
    import javax.imageio.ImageIO;
    import javax.swing.JPanel;
     
     
    public class Panneau extends JPanel {
    	int x;
    	int y;
    	public void paintComponent(Graphics g) {
    		try {
    			Image img = ImageIO.read(new File("images/background.jpeg"));
    			g.drawImage(img,0, 0,800,this.getHeight(), this);
     
    		} catch (IOException e) {
    			e.printStackTrace();
     
    		}
    	try {
    			Image img = ImageIO.read(new File("images/RUBY.png"));
    			g.drawImage(img, 800/8*x, this.getHeight()/8*y,800/8,this.getHeight()/8, this);
     
    		} catch (IOException e) {
    			e.printStackTrace();
     
    		}
     
     
    		g.setColor(Color.black);
    		for (int i=0; i<8; i++) {
    			g.drawLine(800/8*i, 0, 800/8*i, this.getHeight());
    			System.out.println(this.getHeight()/8*i);
    			g.drawLine(0, this.getHeight()/8*i, 800, this.getHeight()/8*i);
    		}
     
     
     
     
    		System.out.println("Je suis exectuée");
    	}
    public void posJoyaux(int c, int u) {
    	this.x=c;
    	this.y=u;
    	this.repaint();
     
    }
    public void remplacerImage(Image im1, String im2) {
    	try {
    		im1=ImageIO.read(new File('"'+im2+'"'));
    	} catch (IOException e) {
    		e.printStackTrace();
    	}}
    	}
    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
    package Robot;
     
     
    import java.awt.event.MouseListener;
    import java.io.File;
    import java.io.IOException;
    import java.awt.Color;
    import java.awt.Graphics;
    import java.awt.Image;
    import java.awt.event.MouseEvent;
     
    import javax.imageio.ImageIO;
    import javax.swing.JButton;
     
     
    public class Bouton extends JButton implements MouseListener {
    String nom;
    boolean app=false;
    public Bouton() {
     
    }
    public Bouton(String nm, boolean ap) {
    	this.nom = nm;
    	this.app=ap;
    	this.setText(nom);
    	this.addMouseListener(this);
     
    }
    public void posBouton(Bouton bout, int x, int y) {
    	bout.setBounds(x, y, 100, 30);
     
    }int i=0;
     
    	public void mouseClicked(MouseEvent event) {
    		i=i+1;
    		System.out.println("app="+this.isApp());
    		this.setApp(true);
    		System.out.println("now app="+this.isApp());
    		this.setText("appuie n"+i);
    		System.out.println("bravo");
     
    	}
     
    	  public boolean isApp() {
    		return app;
    	}
    	public void setApp(boolean app) {
    		this.app = app;
    	}
    	  public void mouseEntered(MouseEvent event) { }
     
    	  public void mouseExited(MouseEvent event) { }
     
     
    	  public void mousePressed(MouseEvent event) { }
     
     
    	  public void mouseReleased(MouseEvent event) { } 
    }

  4. #4
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    septembre 2009
    Messages
    12 373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    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 373
    Points : 29 307
    Points
    29 307
    Billets dans le blog
    2
    Par défaut
    Le souci de base est que ton algorithme consiste à, en boucle :

    1. à lire un attribut, app, par boolean prevapp=but.app;
    2. puis à la comparer à la même valeur par if (but.isApp()!=prevapp){

    isApp() retournant app, il y a peu de chances que cette valeur soit différente d'elle-même. On peut avoir la chance que la première lecture de cette variable intervienne juste avant le clic et le test juste après. Mais pas si la boucle while ne laisse pas de temps à l'Event Dispatch Thread, ou inversement, ce qu'il doit se passer lorsque tu ajoutes le System.out.println().

    En plus, ça tombe en marche en quelque sorte. Parce qu'il y a une optimisation qui fait que la valeur d'une variable écrite par un thread n'est pas immédiatement accessible lors de la lecture de la valeur de la variable dans un autre. Sauf à déclarer la variable volatile ou utiliser des synchronized. Du coup, lorsque tu lis but.app tu ne lis pas la valeur réelle up to date de l'attribut, mais si parfois par .isApp(), ce qu'il se passe lorsque tu laisses la boucle while(true) ne pas consommer tout le temps (en générant une famine pour les autres threads) en introduisant un System.out.println. En fait, il se passe un temps relativement long (plusieurs dizaines de ms) entre le moment où but.app est false et but.isApp() est vrai, donc ce n'est pas du tout efficace, même si ce temps relativement long est finalement court à l'échelle humaine.
    En plus, il peut se passer exactement la même chose avec la position du "joyaux", écrite dans un thread et lue dans l'autre au moment du dessin.


    Le System.out.println("prevapp="+prevapp) doit à priori laisser suffisamment de temps pour lire deux valeurs différentes pour la même variable, ce qui permet au test de passer.

    Le simple ajout d'une pause (au lieu du System.out.println("prevapp="+prevapp)) suffit également à favoriser cette "chance" :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    try {
    	Thread.sleep(100);
    } catch (InterruptedException e) {
    }
    Ou faire un repaint(), ce qui doit suffisamment matraquer la queue de EDT pour qu'elle laisse cette chance passer également.

    Mais bon on ne fait pas comme ça, sinon ça charge le système à mort inutilement, ça rend le fonctionnement reposer sur la chance que ça fonctionne (donc ça ne garantit pas que ça fonctionne partout). Enfin bref, ça n'est pas la bonne manière de faire.

    Enfin, si tu ajoutais dans ton while(true) un ordre de modification de la fenêtre, tu verrais que ça débloque aussi l'affichage, mais ça introduit une énorme latence au démarrage et un fort ralentissement du programme lui-même : ce ralentissement laisse du temps à l'EDT pour rafraîchir l'affichage et au while(true) pour constater parfois la différence nécessaire pour le déplacement.

    Donc quelle est la bonne manière de faire ? Comme il existe de nombreuses mauvaises manières, il existe de nombreuses bonnes manières de faire.

    Tu pourras regarder différents exemples de différentes façons de faire des choses différentes de ce que tu essayes de faire, mais dans le même esprit : animer un truc à l'écran en Swing. Il y en a d'autres, mais elles présentent différentes façons de base (Timer, SwingWorker, etc). Dans ton cas, il n'y a pas vraiment d'animation à priori : si je me base sur ton code, on déplace un mobile d'une case à chaque clic du bouton, donc pas besoin de Timer, de SwingWorker, et encore moins de boucle.



    Voici une solution pour ton programme, la plus simple à mon avis :



    La classe Bouton. Je ne savais pas si tu avais fait changer le nom du bouton juste pour déboguer, donc je l'ai fait, mais il suffit de supprimer l'ajout de l'ActionListener pour ne plus le faire. D'ailleurs, sans ça, cette classe est totalement inutile et on peut directement créer un JButton dans Fenetre.

    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
    package robot;
     
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
     
    import javax.swing.JButton;
     
     
    public class Bouton extends JButton {
     
    	public Bouton(String nom) {
    		this.setText(nom);
     
    	    addActionListener(new ActionListener() {
     
    	    	int nbclicks=0;
     
    			@Override
    			public void actionPerformed(ActionEvent e) {
    				nbclicks++;
    				setText("appui n°"+ nbclicks );
    			}
     
    		});
    	}
     
    }
    Le code de Panneau, avec le chargement des images à l'avance et pas à chaque fois qu'on dessine. J'ai laissé la grille en dur, mais ce serait pas mal qu'elle s'ajuste dynamiquement par rapport à la taille courante :
    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
    package robot;
     
    import java.awt.Color;
    import java.awt.Graphics;
    import java.awt.Image;
    import java.io.File;
    import java.io.IOException;
    import javax.imageio.ImageIO;
    import javax.swing.JPanel;
     
     
    public class Panneau extends JPanel {
     
    	int x;	
    	int y;
     
    	private static Image backgroundImage = readImage("background.jpeg");
    	private static Image rubyImage = readImage("RUBY.png");
    	private static Image readImage(String name) {
    		try {
    			return ImageIO.read(new File("images/"+name));
    		} catch (IOException e) {
    			e.printStackTrace();
    			return null;
    		}
    	}
     
    	public void paintComponent(Graphics g) {
    		super.paintComponent(g);
    		if ( backgroundImage!=null ) {
    			g.drawImage(backgroundImage, 0, 0, this.getWidth(), this.getHeight(), this); 
    		}
     
    		if ( rubyImage!=null ) {
    			g.drawImage(rubyImage, 800/8*x, this.getHeight()/8*y, 800/8, this.getHeight()/8, this);
    		}
     
    		g.setColor(Color.BLACK);
    		for (int i=0; i<8; i++) {
    			g.drawLine(800/8*i, 0, 800/8*i, this.getHeight());
    			g.drawLine(0, this.getHeight()/8*i, 800, this.getHeight()/8*i);
    		}
    	}
     
    	public void posJoyaux(int x, int y) {
    		this.x=x;
    		this.y=y;
    		this.repaint();
    	}
     
    	/*public void remplacerImage(Image im1, String im2) {
    		try {
    			im1 = ImageIO.read(new File('"' + im2 + '"'));
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}*/
     
    }
    La classe Fenetre. Pas de boucle while(true), mais juste un ActionListener pour réagir au clic sur le bouton.
    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
    package robot;
     
    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.GridBagLayout;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
     
    import javax.swing.JFrame;
    import javax.swing.JPanel;
     
    public class Fenetre extends JFrame {
     
    	public Fenetre() {
     
    		this.setTitle("Robot Turtle");
    		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		this.setResizable(false);
     
    		Panneau pan = new Panneau();
    		pan.setBackground(Color.BLUE);
     
    		JPanel buttonPanel = new JPanel(new GridBagLayout()); // ça c'est juste pour placer le bouton au centre de la hauteur
     
    		Bouton but = new Bouton("Jesuisla");
    		but.setPreferredSize(new Dimension(100, 30));
     
    		// l'action listener pour déplacer le "joyaux"
    		but.addActionListener(new ActionListener() {
     
    			private int i=0;
     
    			@Override
    			public void actionPerformed(ActionEvent e) {
    				i++;
    				pan.posJoyaux(i, 2);
    			}
    		});
     
    		this.getContentPane().add(pan);
    		buttonPanel.add(but);
    		this.getContentPane().add(buttonPanel, BorderLayout.EAST);
     
    		this.setSize(1200, 800);
    		this.setLocationRelativeTo(null);
    		this.setVisible(true);
     
    	}
     
    }
    Le code pour lancer le programme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    package robot;
     
    public class Main {
     
    	public static void main(String[] args) {
    		new Fenetre();
    	}
     
    }


    Enfin, je vais prendre point par point différents aspects dans ton programme qui ne vont pas :

    • pourquoi faire une méthode d'instance avec pour argument une instance de la classe elle-même ?
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      public void posBouton(Bouton bout, int x, int y) {
      	bout.setBounds(x, y, 100, 30);
      }
      pourquoi devrait-on passer par une méthode d'une instance de Bouton pour modifier la position d'une autre instance de Bouton ? Si on doit modifier la position de l'instance elle-même, alors pas besoin de la passer en argument :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      public void posBouton(int x, int y) {
      	this.setBounds(x, y, 100, 30);
      }
    • Cela ne sert à rien de recréer des méthodes qui existent déjà dans une API. Éventuellement, si on doit modifier le comportement, on redéfinit les méthodes existantes.
      Donc pas de :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      public void posBouton(Bouton bout, int x, int y) {
      	bout.setBounds(x, y, 100, 30);
       
      }
      Il y a une méthode setLocation() pour changer les coordonnées x et y. Une méthode setSize() pour modifier la taille. Une méthode setBounds() pour modifier les deux à la fois.
      Aussi, s'il ne s'agit de modifier la position, alors pas besoin de modifier aussi la taille. Cela fait toujours ça de code en moins à exécuter inutilement.
      Si le composant a une taille fixe, appeler setSize() une seule fois au début.
    • On évite de supprimer le système d'agencement (les LayoutManager) et de faire du positionnement direct. Avec des LayoutManager, les composants se positionnent et se dimensionnent automatiquement, en fonction de l'environnement (de la taille de l'écran, de la fenêtre, de leurs propres contraintes , etc).
      Donc pas pan.setLayout(null);. Et on appelle jamais setLocation(), setSize() ou setBounds().
    • On évite de redéfinir les méthodes de dessin de Swing, paint(), paintComponent() etc, sans appeler la méthode redéfinie qui va s'occuper correctement du dessin complet du composant, et éviter, entre autres les problèmes relatifs au recyclage du contexte graphique. Même si on redessine une image qui masque tout (background.jpeg dans ton cas). J'espère par ailleurs que cette image ne représente pas un fond uniforme, sinon une couleur de fond serait préférable pour que ça moins lourd pour l'exécution. Une texture dans le fond pourrait être aussi envisagée.
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      try {
      			Image img = ImageIO.read(new File("images/background.jpeg"));
      			g.drawImage(img,0, 0,800,this.getHeight(), this);
       
      		} catch (IOException e) {
      			e.printStackTrace();
       
      		}
      Pourquoi la largeur de l'image est de 800 fixe ? Si le Panneau fait 30px de large, à quoi ça sert de peindre une image de 800px de large ? Même si le clipping rattrape la perte de performance, ça ne sert à rien. Si la hauteur de l'image dépend de la hauteur du Panneau, alors elle est possiblement déformée. Si le Panneau a la bonne taille pour l'éviter, alors la hauteur de l'image peut être fixe.

      Par ailleurs, on évite aussi de charger des fichiers dans les méthodes de dessin (donc dans l'EDT), pour éviter des possibles ralentissement de l'affichage et la gestion de la mémoire. On les charge une fois en mémoire, et on les réutilise.
    • Cette ligne est inutile :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      pan.setBackground(Color.blue);
      Puisque de toute manière tu n'appelle pas super.paintComponent() dans paintComponent() de Panneau, cette couleur de fond n'est pas utilisée.
    • La notion de programmation orientée objet implique qu'un objet doit avoir lui seul la responsabilité de ses états.
      Il ne devrait pas y avoir de setApp dans la classe Bouton. L'état "cliqué" est de la responsabilité du Bouton, pas des autres composants.
    • Une indentation correcte évite les difficultés de lecture, les confusions possibles et favorise le débogage.
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      public void posBouton(Bouton bout, int x, int y) {
      	bout.setBounds(x, y, 100, 30);
       
      }int i=0;

    • Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      public void remplacerImage(Image im1, String im2) {
      	try {
      		im1=ImageIO.read(new File('"'+im2+'"'));
      	} catch (IOException e) {
      		e.printStackTrace();
      	}}
      	}
      Cette méthode ne fait rien d'utile. Puisqu'elle affecte une image chargée à une variable qui est un argument de la méthode, donc une variable locale en résumé, l'image chargée est mise en mémoire, puis plus aucune référence ne permet de l'utiliser (perdue à la fin de l'exécution de la méthode). L'image reste en mémoire jusqu'à ce que le Garbage Collector l'en supprime.

      La concaténation des guillemets autour de im2 va faire que le chemin sera erroné, puisqu'il va devenir "images/turtle4.png". D'ailleurs ton programme affiche une stack trace d'une FileNotFoundException. Tu ne la vois pas parce que tu affiches en continu des traces dans paintComponent.
    • Eviter d'implémenter les listeners dans la classe d'UI, mais plutôt en classe interne, pour ne pas exposer des méthodes qui n'ont pas à être exposées (celles de MouseListener).
      Avec MouseListener, tu peux utiliser la classe MouseAdapter plutôt que d'implémenter l'interface, ce qui te permettra d'écrire uniquement la méthode que tu as besoin, sans faire toutes les autres vides inutilement.
      Aussi, il vaut mieux toujours utiliser les événements de plus haut niveau que ceux de bas niveau. La gestion de la souris est implémentée dans des événements de haut niveau dans les composants standards de Swing, comme le JButton. Inutile de chercher à réinventer son propre système, ce qui évitera des efforts inutiles et une éventuelle implémentation partielle. Pour réagir à un clic sur le bouton, ActionListener. En plus, comme c'est du haut niveau, ça gère tout seul tout ce qui est action sur le bouton, donc clic souris, touche du clavier, écran tactile, etc... et pas seulement la souris.
    • Enfin, il y a des conventions d'écriture en Java :
      1. les noms de package exclusivement en minuscules
      2. les noms de variable commencent par une minuscule, en camelCase exactement
      3. les noms de méthode commencent par une majuscule, en CamelCase exactement
      4. les noms de constantes sont censés être en majuscules, en UPPER_CASE exactement.
        Il y a eu une erreur au début de la création des classes Color, mais elle a été corrigée il y a très longtemps : préférer Color.BLACK plutôt que Color.black
    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.

  5. #5
    Candidat au Club
    Femme Profil pro
    Étudiant
    Inscrit en
    décembre 2019
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 21
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : décembre 2019
    Messages : 6
    Points : 4
    Points
    4
    Par défaut
    Un grand merci à toi ! Je ne sais pas comment te remercier !
    Avec tes modifications tout marche super bien, mais je ne vais pas me contenter de ca, je vais prendre le temps de comprendre chacune de tes remarques pour ne plus refaire ces erreurs. Et même en lisant la première fois j'ai remarqué à quel point je faisait des choses illogique ( comme créer des méthodes déjà existantes) mais la programmation orienté objet c'est nouveau pour moi je pense que j'ai encore l'habitude de vouloir des variables pour tout etc...

    J'aurai une petite question supplémentaire : Comment le programme reconnais que j'attend un 'clic' avec les ActionPerformed ? A quoi sert le mouseClicked alors ?

    Un grand merci à toi ! Merci d'avoir pris le temps de me répondre et surtout merci d'avoir pris le temps de lire tout mon code et de m'avoir indiqué ce qu'il n'allait pas

  6. #6
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    septembre 2009
    Messages
    12 373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    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 373
    Points : 29 307
    Points
    29 307
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par alexiaha Voir le message
    J'aurai une petite question supplémentaire : Comment le programme reconnais que j'attend un 'clic' avec les ActionPerformed ? A quoi sert le mouseClicked alors ?
    Avec un langage de haut niveau comme Java (par opposition aux langages plus proches de la machine, comme le langage machine ou l'assembleur), les interactions avec le processeur, la mémoire, le système, le réseau, les périphériques, etc... ont été implémentés de manière à être utilisés plus simplement au sein d'un programme.

    Pour l’interaction avec les périphériques d'entrées comme le clavier ou la souris, Swing, une couche élevée d'API permettant de réaliser une interface utilisateur graphique, aborde la chose par un système dit événementielle. Chaque composant graphique, comme un JButton (bouton), une JCheckBox (case à cocher), un JTextField (champ de saisie de texte)... est capable d'émettre des réactions à des manipulations de la souris : déplacement, bouton souris appuyé, bouton souris relâché, etc... Il y a de nombreux autres types d'événements qui permettent de réagir à différents types de changement de l'état du système ou de l'interface graphique elle-même, comme le changement de la taille de la fenêtre, l'affichage ou la fermeture d'une fenêtre, le déplacement d'un composant dans l'interface, l'appuie d'une touche du clavier, etc.

    Selon la spécificité du composant, il y a des événements bas niveau, génériques, et des événements plus spécifiques. Par exemple, pour un composant qui affiche plusieurs éléments qu'on peut sélectionner, comme une JComboBox (une liste déroulante de choix) ou une JTable (un composant qui affiche des données en tableau), il existe des événements qui permettent de détecter la sélection d'un élément (ou plusieurs), sans qu'on ait besoin de gérer ça avec les événements bas niveau de gestion de souris, détecter sa position, calculer en fonction de cette position quel est élément qui est survolé, donc cliqué, etc. L'événement de sélection permet de récupérer directement les éléments sélectionnés, sans avoir à faire toute la programmation qui permet de les retrouver.

    Il y a différent niveau d'événements pour chaque composant qui permet de réagir selon les besoins. Certains composants déclenchent une action. Un bouton, au clic, est censé déclencher un sous-programme. La sélection d'un élément dans un JComboBox est censé déclencher une réaction à cette sélection. Lorsqu'on tape entrée dans un JTextField, on déclenche la validation de la saisie. Etc. L'ActionListener permet de réagir de manière spécifique au déclenchement d'une action, sans avoir à programmer toute la logique des différents événements de bas niveau qui amènent au déclenchement de cette action, par rapport à certaines règles d'usage classiques de l'interface utilisateur dans l'environnement spécifiqueque (Windows, MacOSX, ... Android...).

    On peut au besoin tout reprogrammer soi-même en utilisant les événements de bas niveau. Le MouseListener permet de faire ça. Mais il faut savoir que si on le fait, qu'il sera nécessaire de prendre en compte beaucoup de choses qu'on ne connaît pas forcément surtout quand on débute, ou dont on a pas forcément conscience. Par exemple, l'événement de clic du MouseListener, auquel on peut réagir par mouseClicked, qui est déjà d'un certain haut niveau (puisqu'on n'a pas à gérer l'appui du bouton, puis son relâchement, le temps entre ces deux événements pour considérer que c'est un clic, le fait que la souris n'a pas été déplacée hors du composant entre l'appui et le relâchement, etc), va réagir à n'importe quel type de clic, sur n'importe quel bouton (le gauche, le droit, celui du centre) et réagit qu'on clique ou double-clique, ou plus... Sur un JButton, l'action est déclenchée lorsqu'on clique une fois avec un certain bouton (le gauche habituellement, mais ça peut changer selon la personnalisation utilisateur du système). Un clic avec l'autre bouton est censé afficher le cas échéant un menu contextuel en général, pas déclencher l'action.

    Passer par un ActionListener permet d'éviter à tout (re)programmer. En plus, toutes les logiques de déclenchement sont prises en charge (ou du moins un certains nombres, selon l'age de l'API) : par la souris, mais aussi par le clavier, les écrans tactiles, etc. Passer par un MouseListener, c'est comme passer par KeyListener (pour réagir aux manipulations du clavier) et reprogrammer toute la logique, en tenant compte du focus, des composants dit par défaut, etc...

    Donc pour éviter à tout reprogrammer, on utilise toujours l'écouteur de plus haut niveau, et si on a besoin de faire des trucs un peu moins standard, ou ajouter des comportements qui ne sont pas implémentés, on descend dans les niveaux, pour accéder à des niveaux intermédiaires, voire au niveau le plus bas si nécessaire, avec de plus en plus de travail pour obtenir au final un comportement standard cohérent. Parce que le but au final c'est que l'interface fonctionne de manière standard et utilisable par un utilisateur qui a des habitudes et connaît le fonctionnement standard de sa machine. Il faut également tenir compte de l'accessibilité (l'accès à l'interface par des non-voyants par exemple). Les événements de haut niveau ont été pensés pour tenir compte au maximum de toutes les conditions. Les événements de bas niveau laisse le loisir au programmeur de faire ce qu'il veut, mais il ne peut pas penser à tout, ou ne pas connaître tous les tenants et aboutissants, et les différentes problématiques techniques qu'il faut prendre en compte.

    C'est exactement comme l'accès à un serveur web pour afficher un site : on peut tout reprogrammer avec un accès socket de base (voir plus bas niveau réseau si on veut), le protocole HTTP complet, en analysant les flux de caractères, le parsing de HTML, CSS, JavaScript, etc, le rendu du HTML complet, avec l'interprétation des CSS et l'application, du JavaScript, etc. On peut aussi utiliser des API qui implémentent déjà le protocole HTTP, et des API de parsing HTML, CSS, etc et ne faire que l'aspect rendu . On peut aussi utiliser un composant comme la WebView de JavaFX qui fait le rendu dynamique, comme dans un navigateur classique. Plus on descend dans les niveaux, plus on a de travail, plus il faut connaître de choses, et plus on risque d'avoir quelque chose de partiel et bancal, ou ne fonctionnant pas du tout.

    Il est intéressant et utile de connaître les fonctionnements bas niveau, cela permet de comprendre comment tout ça fonctionne. Mais en programmation, de plus en plus, on cherche plutôt à faire des choses de haut niveaux et on n'a pas le temps de tout refaire, surtout que ça ne sert à rien (ça a déjà été fait) et c'est une perte de temps, et ça peut être fastidieux à mettre au point.
    La progression des langages et des systèmes va faire qu'un jour on aura même plus besoin de passer même par un bouton et un ActionListener : on donnera quelques ordres de base pour dire "voilà ce que je veux que l'application fasse et comment". Il y a déjà des systèmes qui proposent différentes approches dans le genre, plus ou moins avancés selon le domaine d'application. Cela permettra de plus s'investir sur l'aspect fonctionnel et moins sur l'aspect technique, en se reposant sur des décades de programmation qui a déjà traité tous ces aspects techniques.
    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.

  7. #7
    Candidat au Club
    Femme Profil pro
    Étudiant
    Inscrit en
    décembre 2019
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 21
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : décembre 2019
    Messages : 6
    Points : 4
    Points
    4
    Par défaut
    Okkk ! Super merci beaucoup !! Tout es beaucoup plus clair pour moi maintenant ! Je comprend mieux ce que j'utilise, ce qui était très flou pour moi auparavant.

    Merci !
    Alexia

Discussions similaires

  1. [PPT-2016] Afficher/masquer des images à l'aide d'un bouton
    Par GwabX dans le forum VBA PowerPoint
    Réponses: 3
    Dernier message: 11/06/2019, 12h33
  2. [Python 3.X] Changer l'image de fond à l'aide d'un bouton
    Par HelloThury dans le forum Tkinter
    Réponses: 4
    Dernier message: 11/08/2014, 13h32
  3. Réponses: 4
    Dernier message: 19/02/2013, 10h38
  4. Réponses: 1
    Dernier message: 12/05/2006, 10h26
  5. ouvrir word a l'aide d'un bouton
    Par Loule dans le forum MFC
    Réponses: 12
    Dernier message: 19/02/2004, 15h55

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