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

  1. #1
    Membre du Club
    Homme Profil pro
    Développeur Java
    Inscrit en
    juin 2015
    Messages
    145
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : juin 2015
    Messages : 145
    Points : 43
    Points
    43
    Par défaut Remplacer un des JPanel d'une JFrame depuis le JPanel
    Bonjour à tous !
    Je reviens ici car je n'arrive pas à trouver la solution à mon problème sur le net. Sans doute à cause du fait que je ne sais pas trop comment formuler ma question.

    Voici mon problème :
    Dans mon programme, j'ai créé une JFrame dans laquelle j'ai inséré 1 JPanel qui contient lui-même 3 JPanel via un BorderLayout (panHaut, panCentral et panBas). Chacun de ces JPanel possède sa propre classe.
    Le JPanel panCentral possède un JLabel et deux JButton agencés via un GridBagLayout.
    Ce que je souhaite c'est que l'un des bouton me permette de remplacer entièrement le contenu du panCentral. Mais je ne vois pas comment procéder.

    Voici un code simplifié de ce que j'ai fait (je ne peux pas poster mon véritable code car il est plus que bordélique, je ne l'ai pas nettoyé et j'ai testé beaucoup de choses que je n'ai pas encore effacé) :

    La JFrame :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class maJFrame extends JFrame {
            private panGeneral pang = new panGeneral();
     
            public maJFrame() {
                    this.setTitle("Test");
    	        this.setSize(1500, 1000);
    	        this.setLocationRelativeTo(null);
    	        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
    	        this.setResizable(false);
    	        this.setContentPane(pang);
    	}
    }
    Le JPanel qui contient les trois autres :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class panGeneral extends JPanel {
            JPanel panHaut = new PanHaut();
            JPanel panCentral = new PanCentral();
            JPanel panBas = new PanBas();
     
            public panGeneral() {
                    this.setLayout(new BorderLayout());
                    this.add(panHaut, BorderLayout.NORTH);
                    this.add(panCentral, BorderLayout.CENTER);
                    this.add(panBas, BorderLayout.SOUTH);
            }
    }
    Le JPanel panCentral qui contient le bouton :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    public class PanCentral extends JPanel {
            JButton bouton = new JButton("Changer de contenu");
     
            public PanCentral() {
                    this.setLayout(new GridBagLayout());
    //je passe le positionnement du bouton...
                    bouton.addActionListener...
    A partir de là je ne sais pas comment m'y prendre. J'ai l'impression que ce qu'il faudrait c'est que dans l'ActionListener (sous ActionPerformed) j'arrive à transformer
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    JPanel panCentral = new PanCentral();
    en
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    JPanel panCentral = new PanCentral2();
    puis ensuite demander à mon JPanel panGeneral de faire un repaint();
    C'est ce qu'il faut faire ?
    Comment on peut faire ça ?

    Je vous remercie d'avance pour votre aide !

  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 309
    Points
    29 309
    Billets dans le blog
    2
    Par défaut
    Salut,

    Ce n'est pas le JPanel panCentral = new PanCentral(); qui place le "pancentral" au milieu du "panGeneral ", c'est le this.add(panCentral, BorderLayout.CENTER);. Donc JPanel panCentral = new PanCentral2();, quelque soit l'endroit où tu comptes le mettre ne suffirait pas à changer le composant central du "panGeneral". Pour changer le composant, il faudrait retirer le composant se trouvant déjà dans le centre (le premier ajouté), puis mettre un nouveau à la place, ce qui oblige le bouton "PanCentral" à avoir accès au PanGeneral, pour appeler une de ses méthodes pour faire ce changement.

    1. Si les différents panels ne sont pas éphémères, et relativement peu nombreux, et qu'il faut passer de l'un à l'autre régulièrement, il est préférable d'utiliser un CardLayout qui est adapté pour afficher alternativement différents composants : on les créer une fois au début, on les place tous dans leur conteneur, et on choisit celui qu'on veut afficher quand on veut. Regarde cette discussion, il y a différents exemples.
    2. L'autre solution consiste donc à faire des méthodes dans PanGeneral, du genre :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      public void displayPanCentral2() {
           this.remove(panCentral); // retire le panneau central actuel
           panCentral = new PanCentral2(this); // créé un nouveau paneau de type 2
           this.add(panCentral); // ajouter le panneau au centre (le BorderLayout.CENTER n'est pas nécessaire dans ce cas)     
           this.revalidate();
           this.repaint();
      }
      les PanCentral et PanCentral2 devrait avoir un paramètre au constructeur de type PanGeneral, pour pouvoir l'invoquer dans l'ActionListener:
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      public class PanCentral {
          private JButton bouton = new JButton("Changer de contenu");
          public PanCentral(PanGeneral parent) {
                      this.setLayout(new GridBagLayout());
      //je passe le positionnement du bouton...
                      bouton.addActionListener(e-> parent.displayPanCentral2());
          }
      }
    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 du Club
    Homme Profil pro
    Développeur Java
    Inscrit en
    juin 2015
    Messages
    145
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : juin 2015
    Messages : 145
    Points : 43
    Points
    43
    Par défaut
    Je pensais qu'en remplaçant "JPanel panCentral = new PanCentral();" par "JPanel panCentral = new PanCentral2();", suite à un repaint() le JPanel général serait reconstruit avec la nouvelle valeur de panCentral.

    En fait, mon programme sera une sorte de jeu dont vous êtes le héros. Quand on lance le programme on tombe sur une fenêtre avec un bandeau en haut qui contiendra diverses infos comme le nom du joueur, un bandeau en bas avec d'autre infos et un contenu central qui contiendra l'histoire et les éléments nécessaires à faire un choix. C'est ce contenu central qui changera constamment au fil du jeu, les bandeaux restent fixes.
    J'ai pensé au CardLayout au début mais il risque d'y avoir beaucoup de panels...

    J'ai dégrossi un peu mon code, je le poste comme ça on parlera tous des mêmes variables, ce sera plus simple.

    Classe "Main"
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    public class Main {
    	public static void main(String[] args) {
    		FenestreMainGame fMainGame = new FenestreMainGame();
    		fMainGame.setVisible(true);
    	}
    }
    Classe "FenestreMainGame" (la JFrame)
    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
    public class FenestreMainGame extends JFrame {
    	private PanMainGame3 panMG = new PanMainGame3();
     
    	Toolkit t = this.getToolkit();
    	Dimension d = t.getScreenSize();
    	int wr = d.width;
    	int hr = d.height;
    	int wf = (wr*80)/100;
    	int hf = (hr*80)/100;
     
    	public FenestreMainGame() {
    	        this.setTitle("Game Test");
    	        this.setSize(wf, hf);
    	        this.setLocationRelativeTo(null);
    	        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
    	        this.setResizable(false);
     
    	        this.setContentPane(panMG);
    	}
    }
    Classe "PanMainGame3" (le JPanel qui contient tous les autres)
    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
    public class PanMainGame3 extends JPanel {
    	JPanel panHaut = new JPanel();
    	JPanel panContenu = new PanMainGame();
    	JPanel panBas = new JPanel();
     
    	Toolkit t = this.getToolkit();
    	Dimension d = t.getScreenSize();
    	int hr2 = d.height;
    	int wr2 = d.width;
    	int wf = (wr2*80)/100;
    	int hf = (hr2*80)/100;
    	double hPanHautC = (hr2*100)/1080;
    	int hPanHaut = (int) hPanHautC;
    	double hPanBasC = (hr2*100)/1080;
    	int hPanBas = (int) hPanBasC;
     
    	double calcecart = (hr2*150)/1080;
    	int ecart = (int) calcecart;
    	double calccols = (wr2*100)/1920;
    	int cols = (int) calccols;
    	double calcwbout = (wr2*300)/1920;
    	int wbout = (int) calcwbout;
    	double calchbout = (hr2*200)/1080;
    	int hbout = (int) calchbout;
    	double calctftitle = (wr2*88)/1920;
    	int tftitle = (int) calctftitle;
    	double calctfbout = (wr2*40)/1920;
    	int tfbout = (int) calctfbout;
     
    	JLabel fake = new JLabel();
    	JLabel fake2 = new JLabel();
     
    	public PanMainGame3() {
     
    		this.setBackground(Color.DARK_GRAY);
    		this.setLayout(new BorderLayout());
     
    		fake.setPreferredSize(new Dimension(1, hPanHaut));
    		panHaut.add(fake);
    		panHaut.setBackground(Color.GRAY);
    		this.add(panHaut, BorderLayout.NORTH);
     
    		fake2.setPreferredSize(new Dimension(100, hPanBas));
    		fake2.setText("En phase de test");
    		fake2.setHorizontalAlignment(SwingConstants.SOUTH_EAST);
    		fake2.setVerticalAlignment(SwingConstants.BOTTOM);;
    		fake2.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 5));
    		panBas.setLayout(new BorderLayout());
    		panBas.add(fake2, BorderLayout.PAGE_END);
    		panBas.setBackground(Color.GRAY);
    		this.add(panBas, BorderLayout.SOUTH);
     
    		panContenu.setBackground(Color.DARK_GRAY);
    		this.add(panContenu, BorderLayout.CENTER);
    	}
     
    	public void displayPanDebut() {
    		this.remove(panContenu);
    		panContenu = new PanDebut();
    		this.add(panContenu);
    		this.revalidate();
    		this.repaint();
    	}
    }
    La classe "PanMainGame" qui est le contenu central amené à être remplacé :
    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
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    public class PanMainGame extends JPanel {
    	JLabel titleGame = new JLabel();
    	JButton newGame = new JButton();
    	JButton loadGame = new JButton();
    	JButton boutbab = new JButton();
    	JButton boutmid = new JButton();
    	JButton bouttrib = new JButton();
     
    	Toolkit t = this.getToolkit();
    	Dimension d = t.getScreenSize();
    	int hr2 = d.height;
    	int wr2 = d.width;
    	double calcecart = (hr2*100)/1080;
    	int ecart = (int) calcecart;
    	double calccols = (wr2*100)/1920;
    	int cols = (int) calccols;
    	double calcwbout = (wr2*300)/1920;
    	int wbout = (int) calcwbout;
    	double calchbout = (hr2*200)/1080;
    	int hbout = (int) calchbout;
    	double calctftitle = (wr2*88)/1920;
    	int tftitle = (int) calctftitle;
    	double calctfbout = (wr2*40)/1920;
    	int tfbout = (int) calctfbout;
     
    	public PanMainGame() {
    		this.setBackground(Color.DARK_GRAY);
     
    		titleGame.setText("EXEMPLE DE TITRE");
    		titleGame.setFont(new java.awt.Font(Font.SERIF, Font.BOLD, tftitle));
    		titleGame.setForeground(Color.BLACK);
    		titleGame.setBackground(Color.GRAY);
    		titleGame.setVerticalAlignment(SwingConstants.NORTH);
    		titleGame.setVisible(true);
     
    		newGame.setText("<html><center>Nouvelle<br>partie</center></html>");
    		newGame.setFont(new java.awt.Font(Font.SERIF, Font.PLAIN, tfbout));
    		newGame.setFocusPainted(false);
    		newGame.setPreferredSize(new Dimension(wbout, hbout));
    		newGame.setBackground(Color.GRAY);
    		newGame.setForeground(Color.BLACK);
    		newGame.setBorder(BorderFactory.createLineBorder(Color.GRAY));
     
    		loadGame.setText("<html><center>Charger<br>une partie</center></html>");
    		loadGame.setFont(new java.awt.Font(Font.SERIF, Font.PLAIN, tfbout));
    		loadGame.setFocusPainted(false);
    		loadGame.setPreferredSize(new Dimension(wbout, hbout));
    		loadGame.setBackground(Color.GRAY);
    		loadGame.setForeground(Color.BLACK);
    		loadGame.setBorder(BorderFactory.createLineBorder(Color.GRAY));
     
    		boutbab.setBackground(Color.DARK_GRAY);
    		boutbab.setPreferredSize(new Dimension(cols, 1));
    		boutbab.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY));
    		boutmid.setBackground(Color.DARK_GRAY);
    		boutmid.setPreferredSize(new Dimension(cols, 1));
    		boutmid.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY));
    		bouttrib.setBackground(Color.DARK_GRAY);
    		bouttrib.setPreferredSize(new Dimension(cols, 1));
    		bouttrib.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY));
     
    		this.setLayout(new GridBagLayout());
     
    		GridBagConstraints gbc = new GridBagConstraints();
     
    		gbc.gridx = 1;
    		gbc.gridy = 2;
    		gbc.gridheight = 1;
    		gbc.gridwidth = 1;
    		this.add(newGame, gbc);
     
    		gbc.gridx = 3;
    		this.add(loadGame, gbc);
     
    		gbc.gridx = 0;
    		gbc.gridy = 1;
    		this.add(boutbab, gbc);
    		gbc.gridx = 4;
    		this.add(bouttrib, gbc);
    		gbc.gridx = 2;
    		this.add(boutmid, gbc);
     
    		gbc.gridx = 0;
    		gbc.gridy = 0;
    		gbc.gridwidth = 5;
    		gbc.fill = GridBagConstraints.VERTICAL;
    		gbc.insets = new Insets(0, 0, ecart, 0);
    		this.add(titleGame, gbc);
     
    		loadGame.addActionListener(new ActionListener() {
    			public void actionPerformed(ActionEvent e) {
    				JFileChooser dialogue = new JFileChooser();
    				dialogue.showOpenDialog(null);
    				System.out.println("Fichier séléctionné : " + dialogue.getSelectedFile());
    			}
    		});
     
    		newGame.addActionListener(new ActionListener() {
    			public void actionPerformed(ActionEvent e) {
    				PanMainGame3.displayPanDebut();
    			}
    		});
    	}
    }
    J'ai intégré ton code.
    Mais du coup, pour chaque changement il doit exister une fonction correspondante dans PanMainGame3. Je pensais qu'il était possible de faire une méthode unique dans laquelle on passe le nom de la classe du nouveau panel en paramètre quand on l'invoque.

    Avec le code que je viens de poster j'ai une erreur dans la classe PanMainGame à la ligne 100 (PanMainGame3.displayPanDebut(); ) : "Cannot make a static reference to the non-static methode displayPanDebut() from the type PanMainGame3". Si je remplace "public PanMainGame() {}" par "public PanMainGame(PanMainGame3 parent) {}" ça ne résoud pas l'erreur précédente et ça rajoute une erreur dans la classe PanMainGame3 à la ligne 3 (JPanel panContenu = new PanMainGame(); ) : "The constructor PanMainGame() is undefined".

    Je pensais que ce serait simple à la base car dans mon précédent programme je changeais de panel aussi mais les boutons étaient dans la JFrame. Changer de panel depuis le panel qui doit disparaître ça devient de suite plus complexe en fait...

  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 309
    Points
    29 309
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Vahia Voir le message
    Je pensais qu'en remplaçant "JPanel panCentral = new PanCentral();" par "JPanel panCentral = new PanCentral2();", suite à un repaint() le JPanel général serait reconstruit avec la nouvelle valeur de panCentral.
    ce code ne fait que déclarer une variable et l'initialiser en créant une instance de classe. Remplace un code par un autre dans le source n'a pas vraiment de sens au runtime. Oui on peut remplacer le premier code par le deuxième, mais le premier panel ne s'affichera jamais plus. On va pas changer le code à chaque fois qu'on affiche l'un ou l'autre panel ! Donc pour faire du dynamique, il faut procéder autrement. Et l'instanciation ne suffira pas à changer le composant dans le container où l'autre composant a été ajouté avant.

    En fait, mon programme sera une sorte de jeu dont vous êtes le héros. Quand on lance le programme on tombe sur une fenêtre avec un bandeau en haut qui contiendra diverses infos comme le nom du joueur, un bandeau en bas avec d'autre infos et un contenu central qui contiendra l'histoire et les éléments nécessaires à faire un choix. C'est ce contenu central qui changera constamment au fil du jeu, les bandeaux restent fixes.
    Citation Envoyé par Vahia Voir le message
    J'ai pensé au CardLayout au début mais il risque d'y avoir beaucoup de panels...
    Beaucoup, c'est combien ? Après, justement, si c'est vraiment beaucoup, l'autre possibilité est valable, elle demande juste un peu plus de code pour que ça marche nickel. Attention en revanche à la taille de la fenêtre : si les panels sont de tailles différentes, ils pourraient influencer la taille de la fenêtre : c'est un peu plus compliqué à gérer du coup.

    Citation Envoyé par Vahia Voir le message
    J'ai dégrossi un peu mon code, je le poste comme ça on parlera tous des mêmes variables, ce sera plus simple.
    Je jetterais un coup d’œil plus tard. Pas le temps pour le moment.

    Citation Envoyé par Vahia Voir le message
    Mais du coup, pour chaque changement il doit exister une fonction correspondante dans PanMainGame3. Je pensais qu'il était possible de faire une méthode unique dans laquelle on passe le nom de la classe du nouveau panel en paramètre quand on l'invoque.
    Oui et non. On peut faire une seule fonction. Il y a plein de possibilités pour le faire, à commencer par la réflexion, les lambdas, etc.

    Citation Envoyé par Vahia Voir le message
    Avec le code que je viens de poster j'ai une erreur dans la classe PanMainGame à la ligne 100 (PanMainGame3.displayPanDebut(); ) : "Cannot make a static reference to the non-static methode displayPanDebut() from the type PanMainGame3". Si je remplace "public PanMainGame() {}" par "public PanMainGame(PanMainGame3 parent) {}" ça ne résoud pas l'erreur précédente et ça rajoute une erreur dans la classe PanMainGame3 à la ligne 3 (JPanel panContenu = new PanMainGame(); ) : "The constructor PanMainGame() is undefined".
    L'appel d'une méthode non static doit être fait sur une instance, pas une classe. Il faut que je regarde ton code pour te dire exactement là où tu fais une erreur dans le passage de paramètres et comme je l'ai dit précédemment, pas le temps pour l'instant.

    Citation Envoyé par Vahia Voir le message
    Je pensais que ce serait simple à la base car dans mon précédent programme je changeais de panel aussi mais les boutons étaient dans la JFrame. Changer de panel depuis le panel qui doit disparaître ça devient de suite plus complexe en fait...
    La position du bouton ne pose pas de souci tant qu'on travaille avec l'ActionListener qui est déclenché après tout traitement dans le bouton (armement, désarmement, tout ça) donc ça ne devrait pas poser de souci.
    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
    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 309
    Points
    29 309
    Billets dans le blog
    2
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    newGame.addActionListener(new ActionListener() {
    			public void actionPerformed(ActionEvent e) {
    				PanMainGame3.displayPanDebut();
    			}
    		});
    Ici tu appelles une méthode d'instance (une méthode non static) en référençant la classe PanMainGame3, alors que c'est une instance de cette classe qu'il faudrait avoir ici. Et c'est justement en la passant au constructeur de PanMainGame que tu pourras l'avoir à disposition.

    Globalement, tu essayes de faire du simili MDA avec des JPanel à l'intérieur d'un JPanel, ce qui n'est pas vraiment un cas d'usage courant et donc il n'y a pas de support tout fait pour faire ça, mais on pourrait faire quelque chose de similaire qui pourrait gérer le truc et simplifier l'écriture du code, à défaut de procéder avec un CardLayout. Quand j'ai un moment je te prépare un brief à ce sujet. En résumé, tu aurais avantage à faire une fabrique qui gère les liens entre composants et leur affichage/"désaffichage".

    Une autre voie serait de faire un composant qui remplacerait le panel courant par un nouveau panel, de manière générique. Je te prépare un brief également dès que j'ai un moment. Ce serait plus simple que le traitement décrit ci-dessus, mais aussi plus restrictif.
    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
    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 309
    Points
    29 309
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par joel.drigo Voir le message
    Une autre voie serait de faire un composant qui remplacerait le panel courant par un nouveau panel, de manière générique. Je te prépare un brief également dès que j'ai un moment. Ce serait plus simple que le traitement décrit ci-dessus, mais aussi plus restrictif.
    Voici une méthode relativement simple, plutôt restrictive, mais qui a le mérite d'être assez simple à invoquer quelque soit le contexte, puisqu'il suffit d'avoir la référence d'un composant (le bouton par exemple), la classe du conteneur général (dans ton cas (le premier), panGeneral, et dans le mien MainPanel), une fabrique de JPanel sous forme de supplier (laissant donc à l'appelant la possibilité d'instancier comme il veut), et la contrainte, juste pour être un peu moins restrictif. Les restrictions sont que le JPanel qui contient les différents panneaux qui se remplacent les uns les autres doit avoir un BorderLayout, et que ces différents panneaux doivent être des JPanel.

    Par ailleurs, comme tu peux le voir, le nombre de conteneur entre le "panneau" à remplacer, et le bouton importe peu.

    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
    public class SwingUtils {
     
    	public static <P extends JPanel, C extends JPanel> void replaceComponentInBorderLayout(JComponent comp, Class<P> c, Supplier<C> supplier, Object constraint) {
     
    		JPanel container = (JPanel) SwingUtilities.getAncestorOfClass(c, comp);
    		if ( container!=null ) {
    			LayoutManager layout = (LayoutManager) container.getLayout();
    			if ( layout instanceof BorderLayout ) {
    				BorderLayout borderLayout = (BorderLayout) layout;
    				Component current = borderLayout.getLayoutComponent(container, constraint);
    				if ( current!=null ) {
    					container.remove(current);
    				}
    				container.add(supplier.get(), constraint);
    				container.revalidate();
    				container.repaint();
    				return;
    			}
    		}
    		throw new IllegalStateException();
     
    	}
     
    }
    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
    66
    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.FlowLayout;
     
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
     
    public class Demo {
     
    	public static void main(String[] args) {
     
    		JFrame frame = new JFrame();
    		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     
    		JPanel content = new MainPanel();
    		content.add(new Panel1()); // au début j'affiche Panel1
     
    		frame.add(content); // plutôt que de remplacer le contentpane qui n'est pas un simple JPanel, j'ajoute mon conteneur principal au centre du content pane
     
    		frame.setSize(800,600);
    		frame.setLocationRelativeTo(null);
    		frame.setVisible(true);
     
    	}
     
            // le conteneur général
    	public static class MainPanel extends JPanel {
    		public MainPanel() {
    			super(new BorderLayout());
    		}
    	}
     
    	public static class Panel1 extends JPanel {
     
    		public Panel1() {
    			setBackground(Color.ORANGE);
    			setLayout(new BorderLayout());
    			JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
    			add(buttonPanel, BorderLayout.SOUTH);
    			JButton button = new JButton("Afficher VERT");
    			buttonPanel.add(button);
                            // le bouton va remplacer le composant au centre du MainPanel par un Panel2
    			button.addActionListener(e-> 
    			SwingUtils.replaceComponentInBorderLayout(button, MainPanel.class, ()-> new Panel2(), BorderLayout.CENTER));
    		}
     
    	}
     
    	public static class Panel2 extends JPanel {
     
    		public Panel2() {
    			setBackground(Color.GREEN);
    			setLayout(new BorderLayout());
    			JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
    			add(buttonPanel, BorderLayout.NORTH);
    			JButton button = new JButton("Afficher ORANGE");
    			buttonPanel.add(button);
                            // le bouton va remplacer le composant au centre du MainPanel par un Panel1
    			button.addActionListener(e-> 
    			SwingUtils.replaceComponentInBorderLayout(button, MainPanel.class, ()-> new Panel1(), BorderLayout.CENTER));
    		}
     
    	}
     
    }
    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
    Membre du Club
    Homme Profil pro
    Développeur Java
    Inscrit en
    juin 2015
    Messages
    145
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : juin 2015
    Messages : 145
    Points : 43
    Points
    43
    Par défaut
    Merci pour ton aide joel.drigo.

    Beaucoup, c'est combien ?
    Il y aura plus d'une cinquantaine de panels je pense. Le nombre exacte n'est pas encore arrêté.

    à commencer par la réflexion, les lambdas, etc.
    Tu me parles chinois, mdr. Je ne connais pas ces notions. J'ai vu passer du lambda il y a peu mais je n'ai pas saisi comment cela s'utilise.

    Globalement, tu essayes de faire du simili MDA avec des JPanel à l'intérieur d'un JPanel
    Je suis venu à imbriquer des panels entre eux car aucun layout manager ne me permettait d'agencer les éléments comme je le voulait. Il fallait cumuler plusieurs layout manager et j'ai utilisé plusieurs panels pour ça.


    Merci pour ton code. Là je suis en famille donc je ne vais pas pouvoir me pencher dessus mais dès que possible je potasse ça et je reviens vers toi.

  8. #8
    Membre régulier Avatar de abdennour bouaicha
    Homme Profil pro
    Développeur Java
    Inscrit en
    avril 2009
    Messages
    93
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Maroc

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : avril 2009
    Messages : 93
    Points : 104
    Points
    104
    Par défaut
    essai ce code ? copier,coller,compiler :
    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
     
    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Rectangle;
     
    import javax.swing.JFrame;
    import javax.swing.JPanel;
     
    class panelText {
        private JFrame    fr = new JFrame( "test JPanel" );
        private Rectangle r  = new Rectangle( 0, 0, 600, 600 );
        private Color     c  = Color.BLUE;
     
        public panelText() {
            fr.getContentPane().setBackground( new Color( 240, 248, 255 ) );
            fr.getContentPane().setLayout( new BorderLayout( 0, 0 ) );
     
            JPanel panel_top = new JPanel();
            panel_top.setBackground( Color.PINK );
            fr.getContentPane().add( panel_top, BorderLayout.NORTH );
     
            JPanel panel_left = new JPanel();
            panel_left.setBackground( Color.YELLOW );
            fr.getContentPane().add( panel_left, BorderLayout.WEST );
     
            JPanel panel_right = new JPanel();
            panel_right.setBackground( Color.BLUE );
            fr.getContentPane().add( panel_right, BorderLayout.EAST );
     
            JPanel panel_bottom = new JPanel();
            panel_bottom.setBackground( Color.GREEN );
            fr.getContentPane().add( panel_bottom, BorderLayout.SOUTH );
     
            JPanel panel_center = new JPanel();
            panel_center.setBackground( Color.RED );
            fr.getContentPane().add( panel_center, BorderLayout.CENTER );
            fr.setBackground( c );
            fr.setDefaultCloseOperation( fr.EXIT_ON_CLOSE );
            fr.setSize( (int) r.getWidth(), (int) r.getHeight() );
            fr.setVisible( true );
            fr.setLocationRelativeTo( null );
        }
     
    }
     
    public class Main {
        public static void main( String[] args ) {
            new panelText();
        }
    }
    moi personnellement je n'aime pas ce genre de codage:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    this.setLayout(....);
    this.add(......);
    je préfère déclarer la jframe comme une variable .

  9. #9
    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 309
    Points
    29 309
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par abdennour bouaicha Voir le message
    essai ce code ? copier,coller,compiler :
    Ce code ne répond pas à la question posée...

    Citation Envoyé par abdennour bouaicha Voir le message
    moi personnellement je n'aime pas ce genre de codage:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    this.setLayout(....);
    this.add(......);
    Qu'est-ce que ça veut dire au juste ? Pourquoi ne pourrait-on pas utiliser ce "genre de codage" ?

    Citation Envoyé par abdennour bouaicha Voir le message
    je préfère déclarer la jframe comme une variable .
    Ce ne sont pas n'importe quelle variable dans ton code : ce sont des attributs (des variables de classe). Donc, tu préfères créer des attributs qui ne ne sont utilisés qu'une seule fois Et puis préférer à quoi ? En quoi ça s'opposerait au "genre de codage" que tu n'aimes pas.

    Et à quoi ça sert de créer une variable de type Rectangle pour donner une dimension à une fenêtre ? Déjà, le type Dimension existe et la variable est superfétatoire.
    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.

  10. #10
    Membre du Club
    Homme Profil pro
    Développeur Java
    Inscrit en
    juin 2015
    Messages
    145
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : juin 2015
    Messages : 145
    Points : 43
    Points
    43
    Par défaut
    Joel.drigo je viens de me pencher sur ton code. Je t'avoue que je n'ai pas tout compris...
    Déjà, je suis épaté que tu obtiennes un tel résultat avec aussi peu de lignes de code.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
    Je ne savais pas qu'on pouvait déclarer un JPanel et son LayoutManager sur la même ligne.

    Ce type d'écriture m'est complètement inconnu. Tu m'avais dit qu'il s'agit des lambdas c'est bien ça ? Je ne connais pas du tout cette notion. Ils n'en parlent même pas dans le bouquin avec lequel j'apprends ("Apprenez à programmer en java, 2e édition" de Cyrille Herby). De mémoire ce bouquin est basé sur Java7.

    En tout cas j'aime beaucoup comme c'est construit. C'est clair et efficace. Je vais essayer de transposer ça dans mon programme dès demain.
    Il va falloir aussi que je creuse plus efficacement comment récupérer des données d'une classe à une autre parce ça me fera gagner beaucoup de lignes de code plutôt que de dupliquer des morceaux dans chaque classe...

  11. #11
    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 309
    Points
    29 309
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Vahia Voir le message
    Je t'avoue que je n'ai pas tout compris...
    N'hésite pas à demander des explications sur les points que tu ne comprends pas.

    Citation Envoyé par Vahia Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
    Je ne savais pas qu'on pouvait déclarer un JPanel et son LayoutManager sur la même ligne.
    C'est parce que la classe JPanel a un constructeur avec un paramètre de type LayoutManager. La JavaDoc est indispensable pour connaître ce genre de possibilités.

    Citation Envoyé par Vahia Voir le message
    Ce type d'écriture m'est complètement inconnu. Tu m'avais dit qu'il s'agit des lambdas c'est bien ça ? Je ne connais pas du tout cette notion. Ils n'en parlent même pas dans le bouquin avec lequel j'apprends ("Apprenez à programmer en java, 2e édition" de Cyrille Herby). De mémoire ce bouquin est basé sur Java7.
    Oui c'est bien une expression lambda et c'est normal qu'on en parle pas dans un bouquin sur Java 7 puisque c'est apparu avec Java 8.
    En fait, il n'y a rien de vraiment compliqué dans une expression lambda. Lorsqu'une interface n'a qu'une seule méthode elle est dite fonctionnelle.
    Habituellement, on peut implémenter une interface soit dans une classe, par exemple l'interface ActionListener :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    public class MonActionListener implements ActionListener {
         public void actionPerformed(ActionEvent event) {
               // le code qu'on veut
               System.out.println("action !");
         }
    }
    ou dans une classe anonyme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    bouton.addActionListener( new ActionListener() {    
         public void actionPerformed(ActionEvent event) {
               // le code qu'on veut
               System.out.println("action !");
         }
    });
    Ici la classe de l'instance créée par le new n'a pas de nom, ce pourquoi on parle de classe anonyme.

    Et bien on peut simplifier cette écriture par une expression lambda, dont la syntaxe est en gros : (les noms des paramètres de la seule méthode de l'interface fonctionnelle)-> le code d'implémentation de la méthode. Lorsqu'il n'y a qu'un seul paramètre, on peut omettre les parenthèse. En revanche, s'il y en a aucun, on est obligé de l'indiquer avec ().
    Ce qui donne dans notre cas :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    bouton.addActionListener( event-> {
               // le code qu'on veut
               System.out.println("action !");
         }
    });
    Lorsque le code n'a qu'une seule instruction, on peut même omettre les accolades et le point-virgule final donc écrire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    bouton.addActionListener( event-> System.out.println("action !"));
    Et en effet ça permet de faire un code beaucoup plus concis.
    A noter que le nom du paramètre (ici event) peut être ce qu'on veut tant qu'il respecte évidement la syntaxe des noms de variables et qu'il n'est pas utilisé comme nom d'une autre variable, comme c'est le cas pour n'importe quelle variable.

    En complément, il y a les références de méthodes qui permettent d'écrire un code encore plus concis. Si le code de l'expression lambda est un appel d'une méthode dont les paramètres se résument à ceux de l'expression lambda (donc ceux de l'unique méthode de l'interface fonctionnelle), on peut utiliser une référence de méthode dont la syntaxe est : propriétaire de la méthode::nom de la méthode.
    Par exemple, si on a (on passe en paramètre de println le paramètre event :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    bouton.addActionListener( event-> System.out.println( event ));
    On peut écrire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    bouton.addActionListener(System.out::println);
    Ce qui fait un code encore plus concis.
    D'ailleurs, au lieu de ()-> new Panel1(), j'aurais pu écrire Panel1::new,

    Il y a encore beaucoup de choses à dire sur le sujet, mais tu les découvriras au fur et à mesure que tu progresseras.

    Citation Envoyé par Vahia Voir le message
    En tout cas j'aime beaucoup comme c'est construit. C'est clair et efficace. Je vais essayer de transposer ça dans mon programme dès demain.
    J'ai fait cette classe SwingUtils avec cette méthode replaceComponentInBorderLayout pour que tu n'aies pas de code à modifier si ce n'est le code dans le bouton "retour" pour l'appeler pour fermer juste l'autre fenêtre.
    Bien sûr rien ne t'empêche de restructurer tout ton code si tu le désires.

    Citation Envoyé par Vahia Voir le message
    Il va falloir aussi que je creuse plus efficacement comment récupérer des données d'une classe à une autre parce ça me fera gagner beaucoup de lignes de code plutôt que de dupliquer des morceaux dans chaque classe...
    Les accesseurs (getter et setter) permettent de lire ou modifier un attribut d'une instance. Ce sont juste des méthodes qu'il faut écrire. En revanche, le plus difficile est de pouvoir accéder à l'instance. Il y a de nombreux moyens, comme par exemple

    • le passage de paramètres au constructeur qui permet lors de l'instanciation (appel d'un new) de passer les références des instances qui permettront l'appel de leurs méthodes
    • la classe de contexte, éventuellement un singleton, dans laquelle on stocke les références des instances avec un moyen de les retrouver. Soit un contexte global dans lequel on met tout, soit des contextes spécifiques.
    • Par la portées des variables (si on donne l'accessibilité à une variable qui stocke une référence par sa portée, alors il est facile de directement appeler une méthode sur celle-ci.
    • Dans certains cas, selon l'API, des moyens plus ou moins simples sont fournis pour accéder aux références de certaines classes, comme dans Swing par exemple, qui permet d'accéder à toute la hiérarchie de composants et de fenêtres à partir de n'importe quel composant. Mais ça peut donner un code hyper fastidieux et difficilement compréhensible et ça peut obliger à tenir compte de l'ordre des événements ce qui peut devenir très complexe à mettre au point.
    • ...
    • Parfois l'API résout le problème de façon simple par un mécanisme dit d'injection qui permet de juste indiquer ce qu'on veut sans avoir à le passer explicitement par paramètre d'un constructeur par exemple, mais il faut que l'API soit faite dans cet esprit, ce qui n'est pas le cas de Swing. On peut ajouter ce mécanisme soi-même ou réaliser un système intermédiaire et c'était la première l'idée que j'évoquais dans mon message. J'essayerai de trouver du temps pour écrire un POC de l'idée
    • ...
    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.

  12. #12
    Membre du Club
    Homme Profil pro
    Développeur Java
    Inscrit en
    juin 2015
    Messages
    145
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : juin 2015
    Messages : 145
    Points : 43
    Points
    43
    Par défaut
    Merci pour toutes ces infos.
    Mais là ça fait beaucoup de choses d'un coup. Je vais traiter les difficultés une à une parce que sinon je ne vais jamais retenir.
    Première chose (la plus facile ), j'ai commandé la nouvelle édition de mon bouquin comme ça j'aurais un support à jour.

    Ensuite ma première difficulté qui, j'en ai bien conscience, fait partie des bases : la récupération de variables depuis l'extérieur de la classe où elles ont été créées.
    Je sais que tu m'as expliqué plus haut mais je n'ai pas compris

    Prenons un code simple :
    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
    public class NouvellesInfos {
            Toolkit t = this.getToolkit();
    	Dimension d = t.getScreenSize();
    	int wr = d.width;
    	int hr = d.height;
     
            public nouvellesInfos() {
                    System.out.println(wr);
                    System.out.println(ht);
    }
     
            public int getHR() {
                    return hr;
    }
     
            public int getWR() {
                    return wr;
    }
    }
    et :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    public class Recup {
            int hr2, wr2;
     
            public recup() {
                    hr2 = NouvellesInfos.getHR();
                    wr2 = NouvellesInfos.getWR();
    }
    }
    Ce code ne fonctionne pas car ça me parle d'une référence static vers du non-static. Je ne suis pas sûr de comprendre ce que cela veut dire...

    Si j'ajoute
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    NouvellesInfos rec = new NouvellesInfos();
    à ma classe Recup puis que je remplace "hr2 = NouvellesInfos.getHR();" par "hr2 = rec.getHR();" (et idem pour wr2), je n'ai plus d'erreur sur les lignes de code. Par contre, à l'exécution eclipse m'envoie un message d'erreur dans la console : "java.lang.StackOverflowError".

    Je suis désolé car je sais que c'est une notion très basique mais je galère...

  13. #13
    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 309
    Points
    29 309
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Vahia Voir le message

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    hr2 = NouvellesInfos.getHR();
    si tu regardes ce qui est avant le .getHR(), tu as un nom de classe. Donc ça voudrait dire que getHR() est une méthode static. Une méthode static est une méthode qui "appartient" à la classe. On peut accèder à des attributs static, des attributs de la classe donc, qui sont en quelque sorte partagée entre toutes les instances (en fait, pas vraiment, mais c'est comme si, et la différence n'est pas importante finalement lorsqu'on programme). En revanche ce qui est important dans cette notion, c'est que chaque instance ne peut pas avoir sa propre valeur de la variable : modifier la valeur pour une instance, c'est modifier la valeur pour toutes les instances. D'ailleurs, pour éviter la confusion, on écrit pas de code qui change la valeur d'une variable static par l'instance et un EDI te mettra un warning. Concrêtement :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    public class Exemple {
        public static int variable;
        public Exemple(int parametre) { // ça c'est le contructeur
            variable = parametre; // on ne fait pas ça
        }
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    Exemple instance1 = new Exemple(5); // je crée une instance
    Exemple instance2 = new Exemple(23); // je crée une autre instance
    System.out.println(instance1.variable); // j'affiche la variable de instance1 (on ne fait jamais ça, variable est static)
    ça va afficher 23, malgré qu'on ait créé instance1 avec la valeur 5.

    A l'inverse :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    public class Exemple {
        public int variable; // remarque bien que j'ai enlevé le static
        public Exemple(int parametre) { // ça c'est le contructeur
            variable = parametre; // on ne fait pas ça
        }
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    Exemple  instance1 = new Exemple(5); // je crée une instance
    Exemple instance2 = new Exemple(23); // je crée une autre instance
    System.out.println(instance1.variable); // j'affiche la variable de instance1
    ça va afficher 5, parce qu'on a créé instance1 avec la valeur 5, et que variable n'est pas static, donc chaque instance a sa propre valeur

    Mais du coup, on ne peut pas écrire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    System.out.println(Exemple.variable);
    ça ne compile pas. parce que variable n'est pas static. Il faut une instance pour pouvoir y accéder, parce que la variable appartient à une instance.

    C'est pareil pour les méthodes. Les méthodes static sont accessibles par la classe (on peut par l'instance, mais il ne faut pas, pour éviter la confusion) et les méthodes pas static sont accessibles par une instance.


    Et c'est bien comme ça que tu la résolu, ou plutôt que tu as tenté de le résoudre :

    Citation Envoyé par Vahia Voir le message

    Par contre, à l'exécution eclipse m'envoie un message d'erreur dans la console : "java.lang.StackOverflowError".

    Je suis désolé car je sais que c'est une notion très basique mais je galère...
    Ça (StackOverflowError), ça veut dire que tu as une méthode qui s'appelle elle-même, donc qui part en une sorte de boucle infinie. Disons pas vraiment infini, parce que ça n'existe pas vraiment dans les ordinateurs vu qu'on a mémoire limitée. Comme l'appel d'une méthode utilise de la pile, et qu'à chaque appel, on en consomme un peu, comme on boucle à l'infini, et bien on arrive à la limite de la taille mémoire allouée à la pile, ce dont Java se plaint par son StackOverflowError = (Error = ) Erreur de (Overflow = ) Dépassement de (Stack =) Pile...

    Bon, dans ce que tu montres, je ne vois pas bien où tu pourrais avoir un dépassement de pile. A priori, tu es censé avoir écrit :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    ublic class Recup {
            int hr2, wr2;
     
            public recup() {
                    NouvellesInfos rec = new NouvellesInfos();
                    hr2 = rec.getHR();
                    wr2 = rec.getWR();
            }
     
    }
    Qui ne devrait pas déclencher de StackOverflowError.

    Le code qui suit pourrait lui :
    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
    public class NouvellesInfos {
            Toolkit t = Toolkit.getDefaultToolkit();
     
    	Dimension d = t.getScreenSize();
    	int wr = d.width;
    	int hr = d.height;
     
            public NouvellesInfos () {
                   new NouvellesInfos(); 
            }
     
            public int getHR() {
                    return hr;
            }
     
            public int getWR() {
                    return wr;
            }
    }
    Parce que dans le constructeur de NouvellesInfos, on fait new NouvellesInfos(), donc on appelle le constructeur de NouvellesInfos donc on fait new NouvellesInfos(), donc on appelle... etc... jusqu'à StackOverflowError !
    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.

  14. #14
    Membre du Club
    Homme Profil pro
    Développeur Java
    Inscrit en
    juin 2015
    Messages
    145
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : juin 2015
    Messages : 145
    Points : 43
    Points
    43
    Par défaut
    Merci beaucoup !
    Je comprends mieux maintenant. En fait je n'avais pas saisi le distingo entre variable de classe et variable d'instance. Le concept d'instance m'échappait un peu.

    Dans mon programme, la classe de la JFrame récupère la taille de l'affichage pour adapter la taille de la fenêtre.
    Je souhaite réutiliser la taille de l'affichage dans plusieurs autre classes afin de redimensionner d'autres choses. Mais je ne souhaite pas utiliser le toolkit à chaque fois, je souhaite juste récupérer les 2 variables déjà existantes (hr pour la hauteur et wr pour la largeur) dans ma JFrame.
    Donc si j'ai bien saisi, étant donné que hr et wr doivent garder leur valeur, elles doivent être déclarées static. Donc du coup on peut les appeler via "nomDeLaClasse.nomDuGetter();.
    Il faut quand même que je continue à creuser les types de variables et leur portée pour que je finisse par être parfaitement à l'aise avec ça...

    Du coup, dans la classe de ma JFrame j'ai déclaré static hr, wr mais aussi les getters associés et les variables que j'utilise pour créer hr et wr :
    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
    public class FenestreMainGame extends JFrame {
    	private PanMainGame3 panMG = new PanMainGame3();
     
    	static Toolkit t = this.getToolkit();
    	static Dimension d = t.getScreenSize();
    	static int wr = d.width;
    	static int hr = d.height;
    	int wf = (wr*80)/100;
    	int hf = (hr*80)/100;
     
    	public FenestreMainGame() {
    		this.setTitle("Game Test");
    		this.setSize(wf, hf);
    		this.setLocationRelativeTo(null);
    		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
    		this.setResizable(false);
     
    		this.setContentPane(panMG);
    	}
     
    	public static int getHeighte() {
    		return hr;
    	}
     
    	public int getWidthe() {
    		return wr;
    	}
    }
    mais là, une chose ne fonctionne pas. C'est le "this". Eclipse me dit que je ne peux pas l'utiliser dans un contexte static. Mais alors dans mon cas, par quoi dois-je le remplacer ?

  15. #15
    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 309
    Points
    29 309
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Vahia Voir le message
    mais là, une chose ne fonctionne pas. C'est le "this". Eclipse me dit que je ne peux pas l'utiliser dans un contexte static. Mais alors dans mon cas, par quoi dois-je le remplacer ?
    Le this est lié au concept d'instance, justement, puisque ça représente l'instance elle-même (je parle du this.variable ou du this.methode(), pas du this() utilisé dans un constructeur, pendant du super()).
    Donc effectivement, ça n'a aucun sens dans un contexte static. Il faut utiliser le nom de la classe. D'ailleurs, il faut savoir qui les méthodes d'instances peuvent être redéfinies, donc en quelque sorte remplacées dans une sous-classe, dans un contexte statique, la redéfinition ne remplace pas la méthode redéfinie dans la super classe. Le nom de la classe permet donc aussi de différencier l'invocation si on veut invoquer spécifiquement la méthode de la super classe.

    Pour ton cas particulier (static Toolkit t = this.getToolkit();), j'ai justement corrigé l'invocation pour la rendre conforme à une invocation static dans l'exemple de mon précédent message.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    Toolkit t = Toolkit.getDefaultToolkit();
     
    	static Dimension d = t.getScreenSize();
    	static int wr = d.width;
    	static int hr = d.height;


    A noter tout de même trois choses (enfin 5 en fait) :

    1. Définir strictement les portées c'est toujours mieux que du systématiquement package. Définir final des variables qui n'ont aucune raison/possibilité de changer de valeur aussi.
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      private final static int WR = d.width;
      private final static int HR = d.heigth;
      Et du coup les getters ne servent à rien : on peut les supprimer et rendre les variables public, pour un accès direct. Du coup, en plus, on profite en plus d'une optimisation : lors de l'utilisation de variable static d'une autre classe, les valeurs sont copiées dans la classe appelant (une sorte d'import static implicite). Ce n'est pas ça qui va accélérer les choses de façon faramineuse, surtout que l'intérêt n'est pas très important dans une IHM, mais c'est toujours ça. Et c'est plus clean. Personnellement, des variables static non final, je n'en ai pas une seule dans aucune de mes applications.
    2. Il faut éviter de stocker des trucs si on ne s'en sert qu'une fois. Ce n'est pas ça qui va saturer la mémoire, mais c'est toujours plus clean
      Seul wr et hr doivent être stockés pour être réutilisables, le reste c'est de l'intermédiaire, à ne pas stocker donc.
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      public final static int WR;
      	public final static int HR;
      	static {
       
      		Toolkit t = Toolkit.getDefaultToolkit();
       
      		Dimension d = t.getScreenSize();
      		WR = d.width;
      		HR = d.height;
       
      	}
    3. Je comprends bien l'intention de stocker les dimensions de l'écran pour dimensionner tes fenêtres, mais pense qu'il y a des gens qui ont deux écrans (voire plus peut-être). De résolutions différentes, éventuellement. Tu ouvres l'application sur un écran et la fenêtre se dimension par rapport à cet écran, puis tu déplaces la fenêtre sur l'autre écran 4 fois plus grand et la fenêtre a toujours la même taille. Du coup, il faut bidouiller pour avoir la taille de fenêtre qu'on veut. On pourrait tester quand la fenêtre change d'écran et redimensionner.
    4. Je comprends bien l'intention de stocker les dimensions de l'écran pour dimensionner tes fenêtres, mais pense qu'il y a des gens qui ont de tous petits écrans et d'autres très grands (ou de grande résolution). Dans un cas, comme dans l'autre, forcer les dimensions des fenêtres va être frustrant pour l'utilisateur : ceux qui auront un petit écran ne verront peut-être pas tout, et ceux qui on un grand écran vont se retrouver avec une UI riquiqui qu'ils ne pourront pas redimensionner à leur convenance.
    5. L'intérêt de stocker les dimensions de manière intermédiaire est finalement moins crucial. Et ce n'est pas trop le temps perdu à le faire qui est très impactant : la taille des fenêtres est donnée à leur ouverture. On peut bien perdre quelques centaines de nano secondes à ce moment pour appeler le toolkit, les dimensions de l'écran, etc. Imposer des dimensions à une fenêtre, après tout, pourquoi pas. Mais on peut aussi faire une méthode du genre :
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      public static void setSizeFromScreen(JFrame frame, int pw, int ph) { 
      	Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
      	frame.setSize( (int) ((d.width * pw)/100f), (int) ((d.height * ph)/100f) );
      }
    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.

  16. #16
    Membre du Club
    Homme Profil pro
    Développeur Java
    Inscrit en
    juin 2015
    Messages
    145
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : juin 2015
    Messages : 145
    Points : 43
    Points
    43
    Par défaut
    je parle du this.variable ou du this.methode(), pas du this() utilisé dans un constructeur, pendant du super()
    Mais du coup, le this() dans un constructeur il fait référence à quoi ?

    Je viens de recevoir la 3e édition du livre "Apprenez à programmer en java". En le feuilletant je découvre que depuis Java8 il existe Java FX. ça a l'air très puissant !
    Je vois qu'on peut mettre en forme sa fenêtre avec du CSS et du XML.
    La question que je me pose c'est est-ce que ça vaut le coup que je continue à tout créer avec awt et Swing ou est-ce qu'il ne vaudrait pas mieux que je bascule sur Java FX et Scene builder ?

  17. #17
    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 309
    Points
    29 309
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Vahia Voir le message
    Mais du coup, le this() dans un constructeur il fait référence à quoi ?
    On ne peut l'utiliser que dans un constructeur, pour faire référence à un autre constructeur de la même classe. Pour invoquer une méthode, on utilise son nom. Mais un constructeur n'a pas vraiment de nom : quand on le déclare, on utilise le nom de la classe, et quand on l'invoque, on ne le fait pas directement, mais par un new, qui utilise aussi le nom de classe. Du coup, pour pouvoir l'invoquer, comme il n'a pas de nom, on utilise le mot this.

    Comme par exemple :

    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
    public class Size {
     
       private final int width;
       private final int heigth;
     
       public Size(int width, int height) {
            this.width=width; // this fait référence à l'instance courante, en cours de construction
            this.height=height; // this fait référence à l'instance courante, en cours de construction
       }
     
       public Size(int size) {
           this(size, size); // this fait référence à un constructeur de la classe courante, dont la sélection se fait par les paramètres (leur nombre, leur type..., comme pour une invocation de méthode).
       }
     
    }
    On pourrait se demander pourquoi les concepteurs de Java n'ont pas permis d'écrire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    public Size(int size) {
           Size(size, size);  
       }
    Je suppose que c'est pour éviter une confusion avec un appel de méthode, d'autant plus qu'on peut déclarer (même si ça ne respecte pas les conventions) une méthode Size(int,int), ce qui empêcherait l'appel du constructeur de signature correspondante.

    Citation Envoyé par Vahia Voir le message
    Je viens de recevoir la 3e édition du livre "Apprenez à programmer en java". En le feuilletant je découvre que depuis Java8 il existe Java FX. ça a l'air très puissant !
    Depuis plus longtemps en fait. Enfin, y'avait une première version, d'esprit et de paradigme complètement différent. Je ne sais plus depuis quand Java FX 2 est sorti, mais, attention, il n'est pas inclus dans le JDK depuis la version 10 sauf erreur.

    Citation Envoyé par Vahia Voir le message
    Je vois qu'on peut mettre en forme sa fenêtre avec du CSS et du XML.
    Oui en effet. En plus son architecture est plus moderne et les dernières normes web (css, html) et média sont gérées (alors que ça fait longtemps que Swing n'a pas évoluer de ce côté).

    Citation Envoyé par Vahia Voir le message
    La question que je me pose c'est est-ce que ça vaut le coup que je continue à tout créer avec awt et Swing ou est-ce qu'il ne vaudrait pas mieux que je bascule sur Java FX et Scene builder ?
    Et bien, c'est une bonne question. Il y a débat en tout cas. Personnellement, en desktop, j'ai il y a longtemps travaillé sous Swing/JGoodies, avant que JavaFX pointe le bout de son nez et avant de pouvoir savoir si ça valait le coup de changer je suis passé à SWT, et depuis 5 ans, je ne fais plus de desktop. Donc je suis assez mal placé pour donner un avis. Je laisse donc la place à d'autres membres du forum qui ont un avis bien plus éclairés pour te donner les avantages et inconvénients d'un choix ou de l'autre. En revanche, si tu n'as pas encore beaucoup de code, il est plus facilement envisageable de partir sur JavaFX maintenant. Plus tu avances en Swing, plus tu auras de travail pour tout refaire.
    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.

  18. #18
    Membre du Club
    Homme Profil pro
    Développeur Java
    Inscrit en
    juin 2015
    Messages
    145
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : juin 2015
    Messages : 145
    Points : 43
    Points
    43
    Par défaut
    Merci pour toutes ces explications. Je pense avoir compris.
    Je me rends compte que j'utilisais tout le temps des variables d'instances en croyant obtenir le comportement des variables de classe. Maintenant c'est plus clair.

    Et bien, c'est une bonne question. Il y a débat en tout cas. Personnellement, en desktop, j'ai il y a longtemps travaillé sous Swing/JGoodies, avant que JavaFX pointe le bout de son nez et avant de pouvoir savoir si ça valait le coup de changer je suis passé à SWT, et depuis 5 ans, je ne fais plus de desktop. Donc je suis assez mal placé pour donner un avis. Je laisse donc la place à d'autres membres du forum qui ont un avis bien plus éclairés pour te donner les avantages et inconvénients d'un choix ou de l'autre. En revanche, si tu n'as pas encore beaucoup de code, il est plus facilement envisageable de partir sur JavaFX maintenant. Plus tu avances en Swing, plus tu auras de travail pour tout refaire.
    Et bien je crois que le choix va s'imposer de lui-même...
    ça fait plus de 3h que je lutte pour faire fonctionner javaFX avec eclipse. C'est pénible.
    Au début les imports ne se faisaient pas. J'ai désinstallé Eclipse et ré-installé la dernière version. Désinstallé le JDK et installé la dernière version (la 13). Installé Scene Builder pour java 11 (c'est la dernière version dispo), installé e(fx)clipse dans eclipse via "http://download.eclipse.org/releases/oxygen" comme indiqué dans mon bouquin. Le problème reste entier.
    Mais vu que dans l'exemple proposé dans le bouquin on supprime les classes et autre fichiers crées d'office avec le projet, je suis passé à la suite. Là je dois créer un fichier fxml mais on me demande un "root element" et je ne peux rien séléctionner ni cliquer sur "browse"...

    Quand je passe plus de temps à me battre avec les programmes plutôt qu'à écrire du code, ça me fait doucement vriller les nerfs.
    Donc je pense que je vais rester sur awt/swing pour ce programme. Je reconsidèrerai la question pour un autre programme. A moins que je trouve une solution rapidement mais c'est pas gagné !

  19. #19
    Membre du Club
    Homme Profil pro
    Développeur Java
    Inscrit en
    juin 2015
    Messages
    145
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : juin 2015
    Messages : 145
    Points : 43
    Points
    43
    Par défaut
    joel.drigo, j'ai encore une petite question pour toi.

    Dans ton post #6, tu appelles une méthode de cette façon :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SwingUtils.replaceComponentInBorderLayout(button, MainPanel.class, ()-> new Panel2(), BorderLayout.CENTER));
    Est-il possible de remplacer "Panel2()" ou "new Panel2()" par quelques chose qui implémenterait la commande ?
    Je m'explique. Imaginons que j'ai fait une sauvegarde dans mon programme. Dans cette sauvegarde j'ai écrit "Panel2()" afin que je puisse récupérer ma position dans le programme.
    Au moment de charger ma sauvegarde pour reprendre le cours de mon programme, je récupère bien mon "Panel2()" et j'aimerais bien l'envoyer directement à la bonne place dans l'appel de méthode précédemment cité. Mais comment faire ?

    J'avais pensé mettre "Panel2()" dans un String mais ça donnerait ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SwingUtils.replaceComponentInBorderLayout(button, MainPanel.class, ()-> new monString, BorderLayout.CENTER));
    mais ça ne marche pas puisque cet appel ne s'attend pas à recevoir un objet String comme paramètre à cet endroit.

    Saurais-tu comment il faudrait que je procède ?

  20. #20
    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 309
    Points
    29 309
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Vahia Voir le message
    j
    Est-il possible de remplacer "Panel2()" ou "new Panel2()" par quelques chose qui implémenterait la commande ?
    [...]

    J'avais pensé mettre "Panel2()" dans un String mais ça donnerait ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SwingUtils.replaceComponentInBorderLayout(button, MainPanel.class, ()-> new monString, BorderLayout.CENTER));
    mais ça ne marche pas puisque cet appel ne s'attend pas à recevoir un objet String comme paramètre à cet endroit.

    Saurais-tu comment il faudrait que je procède ?
    Ce paramètre est du type Supplier, autrement dit un fournisseur de truc et le truc que ça doit fournir doit être d'un type étendant JPanel, comme Panel2 par exemple. On peut écrire :

    • Code : Sélectionner tout - Visualiser dans une fenêtre à part
      SwingUtils.replaceComponentInBorderLayout(button, MainPanel.class, ()-> new Panel2(), BorderLayout.CENTER));
    • Code : Sélectionner tout - Visualiser dans une fenêtre à part
      SwingUtils.replaceComponentInBorderLayout(button, MainPanel.class, Panel2::new, BorderLayout.CENTER));
    • Code : Sélectionner tout - Visualiser dans une fenêtre à part
      SwingUtils.replaceComponentInBorderLayout(button, MainPanel.class, PanelMachin::new, BorderLayout.CENTER));
      tant que PanelMachin est bien une classe qui étend JPanel
    • On pourrait aussi construire au besoin un PanelTruc qui prendrait des paramètres, tant que PanelTruc est une classe qui étend JPanel et qui prendrait en paramètre du constructeur par exemple un Color :
    • Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
       
      Color color = Color.RED;
      SwingUtils.replaceComponentInBorderLayout(button, MainPanel.class, ()-> new PanelTruc(color), BorderLayout.CENTER));
      On peut bien sûr passer plusieurs paramètres : en fait, on est exactement dans la même situation que dans n'importe quel autre code, où tu écrirais (tout ce que tu peux mettre à droite du égal, tu pourrais le mettre à droite du -> (enfin presque, il y une condition dans laquelle ça ne fonctionnerait pas, on verra ça éventuellement le cas échéant) :
      JPanel panel = new MyPanel();
    • etc


    Maintenant, le paramètre est du type Supplier. Donc on peut tout à fait implémenter cette interface dans une classe et utiliser une de ses instances comme supplier :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    public class Panel2Supplier implements Supplier<Panel2> {
        public Panel2 get() {
              return new Panel2();
        }
    }
    puis[*]
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SwingUtils.replaceComponentInBorderLayout(button, MainPanel.class, new Panel2Supplier(), BorderLayout.CENTER));
    Maintenant tu dis que tu as une String avec le nom de la classe. Il se trouve qu'il y un moyen en Java d'instancier une classe si on a le nom de la classe sous forme de String. Cela s'appelle la reflection ! Aussi faut-il avoir le nom complet (donc package compris). Mais ce n'est pas vraiment un problème, si tu sais comment le trouver à partir du nom court (en concaténant le nom du package tu retrouves le nom complet).

    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
    public class ReflectionPanelSupplier implements Supplier<JPanel> {
     
    	private String className; 
     
    	public ReflectionPanelSupplier(String className) {
    		this.className=className;
    	}
     
    	@Override
    	public JPanel get() {
     
    		// reflection
     
    		try {
    			Class<?> k = Class.forName(className);// permet d'obtenir une classe à partir de son nom dans une String
     
    			if ( JPanel.class.isAssignableFrom(k) ) { // on vérifie qu'il s'agit bien d'une classe étendant JPanel
    				return (JPanel) k.newInstance(); // on instancie la classe // si ta classe a des paramètres, on peut aussi faire de la reflection. c'est un tout petit peu plus compliqué. Dis-moi si tu as en as besoin
    			}
    			else {
    				throw new ClassCastException(className + " is not assignable from JPanel");
    			}
     
    		} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
    			throw new RuntimeException(e);
    		} 
     
     
    	}
     
    }
    J'ai modifié mon code de démo pour utiliser ce composant pour créer mes panels à partir d'une String :

    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
    public class Demo {
     
    	public static void main(String[] args) {
     
    		JFrame frame = new JFrame();
    		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     
    		JPanel content = new MainPanel();
    		content.add(new Panel1()); // au début j'affiche Panel1
     
    		frame.add(content); // plutôt que de remplacer le contentpane qui n'est pas un simple JPanel, j'ajoute mon conteneur principal au centre du content pane
     
    		frame.setSize(800,600);
    		frame.setLocationRelativeTo(null);
    		frame.setVisible(true);
     
    	}
     
            // le conteneur général
    	public static class MainPanel extends JPanel {
    		public MainPanel() {
    			super(new BorderLayout());
    		}
    	}
     
    	public static class Panel1 extends JPanel {
     
    		public Panel1() {
    			setBackground(Color.ORANGE);
    			setLayout(new BorderLayout());
    			JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
    			add(buttonPanel, BorderLayout.SOUTH);
    			JButton button = new JButton("Afficher VERT");
    			buttonPanel.add(button);
                            // le bouton va remplacer le composant au centre du MainPanel par un Panel2
    			button.addActionListener(e-> 
    			SwingUtils.replaceComponentInBorderLayout(button, MainPanel.class, new ReflectionPanelSupplier("Demo$Panel2"), BorderLayout.CENTER));
    		}
     
    	}
     
    	public static class Panel2 extends JPanel {
     
    		public Panel2() {
    			setBackground(Color.GREEN);
    			setLayout(new BorderLayout());
    			JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
    			add(buttonPanel, BorderLayout.NORTH);
    			JButton button = new JButton("Afficher ORANGE");
    			buttonPanel.add(button);
                            // le bouton va remplacer le composant au centre du MainPanel par un Panel1
    			button.addActionListener(e-> 
    			SwingUtils.replaceComponentInBorderLayout(button, MainPanel.class, new ReflectionPanelSupplier("Demo$Panel1"), BorderLayout.CENTER));
    		}
     
    	}
     
    }
    Dans cet exemple, le nom de mes classes de panel commencent par "Demo$". Parce qu'il s'agit de classes statiques internes à la classe Demo, mais je n'ai pas mis la classe Demo dans un package, donc son nom complet, par exemple pour la classe Panel2, c'est Demo$Panel2.

    Maintenant on pourrait faire plus simple si tes panels sont en nombre limité. Au lieu d'utiliser la réflexion, on va simplement énumérer les suppliers dont on a besoin, pour couvrir le besoin. Et on va utiliser une enum.

    Moi j'ai deux panels, c'est plutôt peu :
    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
    public enum PanelSupplierFactory implements Supplier<JPanel> {
     
    	PANEL1 {
    		@Override
    		public JPanel get() {
    			return new Demo.Panel1();
    		}
    	},
    	PANEL2 {
    		@Override
    		public JPanel get() {
    			return new Demo.Panel2();
    		}
    	},
    	; 
     
    	public static Supplier<JPanel> from(String name) {
    		return valueOf(name.toUpperCase());
    	}
     
    }
    Avec la démo :
    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
    public class Demo {
     
    	public static void main(String[] args) {
     
    		JFrame frame = new JFrame();
    		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     
    		JPanel content = new MainPanel();
    		content.add(new Panel1()); // au début j'affiche Panel1
     
    		frame.add(content); // plutôt que de remplacer le contentpane qui n'est pas un simple JPanel, j'ajoute mon conteneur principal au centre du content pane
     
    		frame.setSize(800,600);
    		frame.setLocationRelativeTo(null);
    		frame.setVisible(true);
     
    	}
     
            // le conteneur général
    	public static class MainPanel extends JPanel {
    		public MainPanel() {
    			super(new BorderLayout());
    		}
    	}
     
    	public static class Panel1 extends JPanel {
     
    		public Panel1() {
    			setBackground(Color.ORANGE);
    			setLayout(new BorderLayout());
    			JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
    			add(buttonPanel, BorderLayout.SOUTH);
    			JButton button = new JButton("Afficher VERT");
    			buttonPanel.add(button);
                            // le bouton va remplacer le composant au centre du MainPanel par un Panel2
    			button.addActionListener(e-> 
    			SwingUtils.replaceComponentInBorderLayout(button, MainPanel.class, PanelSupplierFactory.from("panel2"), BorderLayout.CENTER));
    		}
     
    	}
     
    	public static class Panel2 extends JPanel {
     
    		public Panel2() {
    			setBackground(Color.GREEN);
    			setLayout(new BorderLayout());
    			JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
    			add(buttonPanel, BorderLayout.NORTH);
    			JButton button = new JButton("Afficher ORANGE");
    			buttonPanel.add(button);
                            // le bouton va remplacer le composant au centre du MainPanel par un Panel1
    			button.addActionListener(e-> 
    			SwingUtils.replaceComponentInBorderLayout(button, MainPanel.class, PanelSupplierFactory.from("panel1"), BorderLayout.CENTER));
    		}
     
    	}
     
    }
    L'avantage avec cette enum, c'est qu'on peut aussi utiliser les valeurs d'enum :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SwingUtils.replaceComponentInBorderLayout(button, MainPanel.class, PanelSupplierFactory.PANEL2 , BorderLayout.CENTER));
    En revanche, ça risque d'être moins pratique s'il y a des paramètres à la construction.


    Bon deux panels c'est vraiment très peu. Du coup, on peut faire encore plus simple, en faisant une classe avec une méthode utilitaire qui teste juste la String et produit le supplier qui correspond :

    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
    public class PanelUtils {
     
    	public static <P extends JPanel> void replaceComponentInBorderLayout(JComponent comp, Class<P> c, String id, Object constraint) {
    		Supplier<JPanel> supplier;
    		switch(id.toLowerCase()) {
    		case "panel1":
    			supplier = Demo.Panel1::new;
    			break;
    		case "panel2":
    			supplier = Demo.Panel2::new;
    			break;
    		default:
    			throw new IllegalStateException("Unknown: " + id);
    		}
    		SwingUtils.replaceComponentInBorderLayout(comp, c, supplier, constraint);
    	}
     
    }
    Ainsi, on l'appelle :

    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
    public class Demo {
     
    	public static void main(String[] args) {
     
    		JFrame frame = new JFrame();
    		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     
    		JPanel content = new MainPanel();
    		content.add(new Panel1()); // au début j'affiche Panel1
     
    		frame.add(content); // plutôt que de remplacer le contentpane qui n'est pas un simple JPanel, j'ajoute mon conteneur principal au centre du content pane
     
    		frame.setSize(800,600);
    		frame.setLocationRelativeTo(null);
    		frame.setVisible(true);
     
    	}
     
            // le conteneur général
    	public static class MainPanel extends JPanel {
    		public MainPanel() {
    			super(new BorderLayout());
    		}
    	}
     
    	public static class Panel1 extends JPanel {
     
    		public Panel1() {
    			setBackground(Color.ORANGE);
    			setLayout(new BorderLayout());
    			JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
    			add(buttonPanel, BorderLayout.SOUTH);
    			JButton button = new JButton("Afficher VERT");
    			buttonPanel.add(button);
                            // le bouton va remplacer le composant au centre du MainPanel par un Panel2
    			button.addActionListener(e-> 
    			PanelUtils.replaceComponentInBorderLayout(button, MainPanel.class, "panel2", BorderLayout.CENTER));
    		}
     
    	}
     
    	public static class Panel2 extends JPanel {
     
    		public Panel2() {
    			setBackground(Color.GREEN);
    			setLayout(new BorderLayout());
    			JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
    			add(buttonPanel, BorderLayout.NORTH);
    			JButton button = new JButton("Afficher ORANGE");
    			buttonPanel.add(button);
                            // le bouton va remplacer le composant au centre du MainPanel par un Panel1
    			button.addActionListener(e-> 
    			PanelUtils.replaceComponentInBorderLayout(button, MainPanel.class, "panel1", BorderLayout.CENTER));
    		}
     
    	}
     
    }
    Enfin, on va même se servir de ça pour afficher le Panel1 dès le début. Il faut juste faire un petit changement dans SwingUtils parce qu'au début on n'a pas encore de "panel" dans MainPanel, donc pas de bouton non plus.

    J'ai juste modifié le début :

    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
    public class SwingUtils { 
     
    	public static <P extends JPanel, C extends JPanel> void replaceComponentInBorderLayout(JComponent comp, Class<P> c, Supplier<C> supplier, Object constraint) {
     
    		JPanel container;
    		if ( c.isInstance(comp) ) { // si le composant est déjà du type "P", alors on utilise ça comme container
    			container = (JPanel) comp;
    		}
    		else {
    			container = (JPanel) SwingUtilities.getAncestorOfClass(c, comp);
    		}
    		if ( container!=null ) {
    			LayoutManager layout = (LayoutManager) container.getLayout();
    			if ( layout instanceof BorderLayout ) {
    				BorderLayout borderLayout = (BorderLayout) layout;
    				Component current = borderLayout.getLayoutComponent(container, constraint);
    				if ( current!=null ) {
    					container.remove(current);
    				}
    				container.add(supplier.get(), constraint);
    				container.revalidate();
    				container.repaint();
    				return;
    			}
    		}
    		throw new IllegalStateException();
     
    	}
     
    }
    Et donc (je te remets que la méthode main :

    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
    public static void main(String[] args) {
     
    		JFrame frame = new JFrame();
    		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     
    		JPanel content = new MainPanel();
    		PanelUtils.replaceComponentInBorderLayout(content, MainPanel.class, "panel1", BorderLayout.CENTER);
     
     
    		frame.add(content); // plutôt que de remplacer le contentpane qui n'est pas un simple JPanel, j'ajoute mon conteneur principal au centre du content pane
     
    		frame.setSize(800,600);
    		frame.setLocationRelativeTo(null);
    		frame.setVisible(true);
     
    }
    Bon la méthode ne remplace plus tout à fait ici, il faudrait éventuellement la renommer, ou faire une version spéciale qui ne remplace pas mais place un JPanel dans un conteneur, pour ce cas.


    Et on obtient quelque chose qui ressemble pas mal à ce que tu cherchais à faire, non ?
    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.

Discussions similaires

  1. Comment provoquer le pack() d'une Jframe depuis un JPanel sans ref sur la JFrame
    Par cdtkoenig dans le forum Agents de placement/Fenêtres
    Réponses: 4
    Dernier message: 21/07/2008, 20h11
  2. Réponses: 2
    Dernier message: 19/12/2006, 23h55
  3. Adaptation dynamique des composants d'une JFrame
    Par Invité dans le forum Agents de placement/Fenêtres
    Réponses: 2
    Dernier message: 06/12/2006, 23h23
  4. Insérer des données dans une BD depuis un fichier .bat
    Par kurkaine dans le forum SQL Procédural
    Réponses: 3
    Dernier message: 24/11/2006, 09h31
  5. Réponses: 5
    Dernier message: 07/06/2006, 13h11

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