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

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

AWT/Swing Java Discussion :

Dessiner de "n'importe où" sur un JPanel avec Graphics


Sujet :

AWT/Swing Java

  1. #1
    Membre averti
    Avatar de smarlytomtom
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Novembre 2014
    Messages
    139
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Territoire de Belfort (Franche Comté)

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2014
    Messages : 139
    Points : 373
    Points
    373
    Billets dans le blog
    1
    Par défaut Dessiner de "n'importe où" sur un JPanel avec Graphics
    Salut à tous !
    Je ne sais pas si le titre est très évocateur de ce que je veux faire, mais je vais développer tout ça !

    Je m'amuse actuellement avec la librairie standard et plus précisément avec les Swing et AWT ! Ce que je cherche à faire est de dessiner une Map grâce à un fichier texte et un tileset, une tiled Map en gros. Cette étape est déjà fait et voilà ce que ça donne :

    Nom : Capture.PNG
Affichages : 1068
Taille : 38,2 Ko

    Jusqu'ici pas de problème ! Mais dés que je redimensionne la fenêtre, il se passe la chose suivante :

    Nom : cap.PNG
Affichages : 981
Taille : 7,5 Ko

    Tout disparaît, ce que je comprends parfaitement car je rends ma map de la façon suivante :

    Ma classe 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
    17
    18
    19
     
     
    import fr.ds.display.Display;
    import fr.ds.map.MapParser;
    import fr.ds.map.MapRenderer;
     
    public class Main {
     
    	public static void main(String[] args) {
     
    		Display display = new Display("Map tests");
    		display.create();
     
    		MapRenderer renderer = new MapRenderer(new MapParser("test.map").createMap());
    		renderer.render(display.getContentPanel());	
     
    	}
     
    }
    Ma classe MapRender qui permet le rendu de ma map:
    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
     
     
    import javax.swing.JPanel;
     
    public class MapRenderer {
     
    	private Map map;
    	private Tile tileset;
     
    	private final int tileSize = 32;
     
    	public MapRenderer(Map map){
     
    		this.map = map;
    		this.tileset = new Tile(map.getTileset());
     
    	}
     
    	public void render(JPanel mapView){
     
    		for(int x = 0; x < map.getMapContent().size(); x++){
     
    			for(int y = 0; y < map.getMapContent().get(x).size(); y++){
     
    				int element = map.getMapContent().get(x).get(y);
    				mapView.getGraphics().drawImage(tileset.getImg(), x * this.tileSize,
    						y * this.tileSize,
    						(x * this.tileSize) + this.tileSize,
    						(y * this.tileSize) + this.tileSize,
    						element * this.tileSize,
    						0,
    						(element * this.tileSize) + this.tileSize,
    						this.tileSize,
    						null);
     
    			}
     
    		}
     
    	}
     
    }
    Voilà donc je sais pourquoi ça ne reste pas lors de "l'actualisation" de la fenêtre et je me doute que ce n'est pas la bonne solution du tout. Ce problème ma amené à me demander comment je pourrais, depuis n'importe ou dans le code, ajouter un élément dans mon JPanel ou alors dessiner sur mon JPanel.

    J'ai donc tenter de redessiner le tout dans une boucle mais ce n'est pas très beau ! En effet lors du "resize" de la fenêtre j'observe des glitchs visuelles qui ne sont pas plaisant !
    J'espère que j'ai été assez clair, dans le cas contraire n'hésitez pas à me demander des précisions !
    Thomas Gredin.
    Développeur Unity 3D/VR

    Mon site personnel : http://thomasgredin.com/fr
    Mon portfolio : http://thomasgredin.com/fr/portfolio

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

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

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

    On n'utilise jamais le contexte graphique d'un composant par la méthode getGraphics(). Le principe de AWT/Swing est de dessiner dans le context graphique par la méthode paint() et des méthodes appelées par cette méthode, comme paintComponent() par exemple. Cette méthode étant appelée par Swing au moment où il le juge opportun, tout ce que tu auras dessiné de la manière que tu le fais sera écrasé. Aussi, il faut savoir que le contexte graphique est récyclé, pour des raisons d'optimisation : ceci signifie qu'il peut être utilisé pour dessiner n'importe quel composant, comme le bouton à gauche, au moment où SWING le juge opportun, donc même si tu ne redimensionnes pas ta fenêtre, un clic sur le bouton pourrait faire apparaître le bouton sur ta map. En plus, ton code est exécuté dans le thread standard de l'application, alors que tout ce qui dessiné par SWING est fait dans un thread spécifique, l'Event Distpach Thread (ou EDT) : ceci pourrait causer des interférences, puisque ton dessin ne va pas se faire nécessairement au rythme que SWING, donc tu pourrais dessiner ta map au moment où SWING utilise le contexte graphique pour dessiner autre chose que le fond du panel, et tu pourrais donc voir des bouts de ta map dans le bouton, par exemple.

    Donc il faut que tu redéfinissent la méthode paintComponent(). Dans cette méthode, par contre, tu peux déléguer le dessin à un autre composant, en lui passant le contexte graphique (l'instance de Graphics) en paramètre. Attention, toutefois, à ce que cette partie ne prennent pas trop de temps, pour ne pas freiner l'UI. En effet, l'EDT, comme tout thread, ne peut faire qu'une seule chose à la fois. Donc plus ça prend de temps pour afficher ta map, plus l'affichage globale et l'interaction (la souris, le clavier) seront bloqué longtemps. Il vaut donc mieux faire ce qui est lourd dans un thread à part, pour préparer une map très rapide à s'afficher (un tableau d'images par exemple), et ensuite demander le rafraîchissement du composant, par appel de repaint.)

    Par exemple, tu fais une classe :

    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 MapPanel extends JPanel {
     
       private volatile Image[][] tableau;
     
     
       public void load(File file) {
              // ici construire le tableau d'image dans une variable locale (pour ne pas être en conflit) 
              this.tableau = loadTableau(file);; // tranfert du tableau 
              repaint(); // redessin
       }
     
       protected void paintComponent(Graphics g) {
     
              super.paintComponent(g);
     
              // ici dessiner le tableau d'image (ici tu peux déléguer à un MapRenderer si tu veux)
              final int largeurImage  = ...
              final int hauteurImage = ...
              int y=0;
              for(Image[] ligne : tableau) {    
                   int x=0; 
                   for(Image image : ligne) {       
                         g.drawImage(...);
                         x+=largeurImage;
                   }
                   y+=hauteurImage;
              }
     
       }
     
    }
    A noter que si ta map est static, il sera avantageux de faire le rendu dans le Graphics d'une image (BufferedImage) et d'afficher cette image en fond du JPanel, ça ira beaucoup plus vite, en particulier si tu comptes dessiner ensuite par dessus la map, des éléments (personnages qui bougent par exemple).
    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 averti
    Avatar de smarlytomtom
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Novembre 2014
    Messages
    139
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Territoire de Belfort (Franche Comté)

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2014
    Messages : 139
    Points : 373
    Points
    373
    Billets dans le blog
    1
    Par défaut
    Merci drigo !
    Ta solution fonctionne très bien ! Cependant est-ce la seul solution pour dessiner ? Ce que je veux dire par la c'est est-ce que je suis obliger de créer un nouveau JPanel pour chaque type de rendu que je souhaite ?n'y à t'il pas un moyen de rendre les choses vraiment à partir de n'importe quel endroit dans le code ?

    Par exemple avec une référence vers le JPanel voulu, ou je pourrais "compléter" en quelques sorte la méthode paintComponent(Graphics g) ? C'est surement impossible mais je préfère demander on ne sait jamais !
    Thomas Gredin.
    Développeur Unity 3D/VR

    Mon site personnel : http://thomasgredin.com/fr
    Mon portfolio : http://thomasgredin.com/fr/portfolio

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

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

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Points : 29 131
    Points
    29 131
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par smarlytomtom Voir le message
    Merci drigo !
    Ta solution fonctionne très bien ! Cependant est-ce la seul solution pour dessiner ? Ce que je veux dire par la c'est est-ce que je suis obliger de créer un nouveau JPanel pour chaque type de rendu que je souhaite ?n'y à t'il pas un moyen de rendre les choses vraiment à partir de n'importe quel endroit dans le code ?

    Par exemple avec une référence vers le JPanel voulu, ou je pourrais "compléter" en quelques sorte la méthode paintComponent(Graphics g) ? C'est surement impossible mais je préfère demander on ne sait jamais !
    Oui, c'est possible, mais pas par accès direct au Graphics : si tu fais un composant de délégation, tu peux changer ce composant de délégation.

    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
    public class DemoDessin extends JPanel {
     
    	private static final Color[] COLORS = {Color.BLUE, Color.GREEN, Color.YELLOW, Color.MAGENTA };
     
    	private Optional<IRenderer> rendererHandler = Optional.empty();
     
    	public DemoDessin() { 
    	}
     
    	public void setRenderer(IRenderer renderer) {
    		this.rendererHandler = Optional.ofNullable(renderer);
    		repaint();
    	}
     
    	@Override
    	protected void paintComponent(Graphics g) {
    		super.paintComponent(g);
    		g.setColor(getForeground());
    		rendererHandler.ifPresent(renderer-> renderer.draw((Graphics2D)g, getWidth(), getHeight()));
    	}
     
    	@FunctionalInterface
    	public static interface IRenderer {
     
    		void draw(Graphics2D g, int width, int height);
     
    	}
     
    	public static void main(String[] args) {
     
    		JFrame frame = new JFrame("Démo");
    		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     
    		DemoDessin panel = new DemoDessin();
    		panel.setForeground(Color.ORANGE);
    		panel.setBackground(Color.WHITE);
     
    		frame.add(panel);
     
    		JPanel buttonPanel = new JPanel();
    		frame.add(buttonPanel, BorderLayout.SOUTH);
     
    		final IRenderer drawOval = (g,w,h)-> g.fillOval(10,10,w-20,h-20);
    		final IRenderer drawSquare = (g,w,h)-> g.fillRect(10,10,w-20,h-20);
    		final IRenderer drawDiamond = (g,w,h)-> fillDiamond(g,10,10,w-20,h-20);
     
    		buttonPanel.add(createButton("Cercle", e-> panel.setRenderer(drawOval) ));
    		buttonPanel.add(createButton("Carré", e-> panel.setRenderer(drawSquare) ));
    		buttonPanel.add(createButton("Losange", e-> panel.setRenderer(drawDiamond) ));
    		AtomicInteger index = new AtomicInteger();
    		buttonPanel.add(createButton("Défiler", e-> panel.setRenderer(getNext(index, drawOval, drawSquare, drawDiamond)) ));
     
     
    		frame.setSize(400, 300);
    		frame.setLocationRelativeTo(null);
    		frame.setVisible(true);
     
    	}
     
    	private static IRenderer getNext(AtomicInteger index, IRenderer...renderers) {
    		int i=index.getAndIncrement();
    		return (g,w,h)-> {
    			g.setColor(COLORS[i%COLORS.length]);
    			renderers[i%renderers.length].draw(g, w, h);
    		};
    	}
     
    	private static void fillDiamond(Graphics2D g, int x, int y, int w, int h) {
    		Polygon polygon = new Polygon(new int[]{x,x+w/2,x+w,x+w/2},
    									  new int[]{y+h/2,y,y+h/2,y+h},
    									  4);
    		g.fill(polygon);
    	}
     
    	private static JButton createButton(String text, ActionListener listener) {
    		JButton button = new JButton(text);
    			button.addActionListener(listener);
    			return button;
    	}
     
    }
    Là, je n'ai bien qu'un seul panel, lequel je fais dessiner différents trucs, soit par des boutons différents (les trois premiers), soit par un seul bouton, le quatrième.
    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
    Membre averti
    Avatar de smarlytomtom
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Novembre 2014
    Messages
    139
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Territoire de Belfort (Franche Comté)

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2014
    Messages : 139
    Points : 373
    Points
    373
    Billets dans le blog
    1
    Par défaut
    Aie ! Je remarque qu'il y à vraiment beaucoup de notions que j'ignore encore ! Je vais étudier ton code avec grande attention ! Je te remercie pour ton aide !
    Thomas Gredin.
    Développeur Unity 3D/VR

    Mon site personnel : http://thomasgredin.com/fr
    Mon portfolio : http://thomasgredin.com/fr/portfolio

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

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

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Points : 29 131
    Points
    29 131
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par smarlytomtom Voir le message
    Aie ! Je remarque qu'il y à vraiment beaucoup de notions que j'ignore encore !
    N'hésite pas à demander des explications.
    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.

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

Discussions similaires

  1. Dessiner la webcam sur un JPanel
    Par RhO dans le forum Multimédia
    Réponses: 2
    Dernier message: 25/08/2009, 00h02
  2. Réponses: 4
    Dernier message: 21/07/2008, 22h32
  3. dessiner sur un JPanel
    Par bellemlih dans le forum Interfaces Graphiques en Java
    Réponses: 4
    Dernier message: 15/02/2008, 11h06
  4. Réponses: 2
    Dernier message: 03/03/2007, 19h16
  5. Réponses: 4
    Dernier message: 27/02/2006, 16h32

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