Question

Pour les deux derniers jours, j'ai essayé de comprendre comment Java gère graphiques, mais ont lamentablement échoué à cela. Mon problème principal est de comprendre exactement comment et quand la peinture () (ou le plus récent paintComponent ()) est / doit être appelé.

Dans le code suivant j'ai fait voir quand les choses sont créées, le paintComponent () est jamais appelé, à moins que j'ajouter manuellement un appel à moi-même ou les appels vers JFrame.paintAll () / JFrame.paintComponents ().

Je renomme la méthode de peinture () pour paintComponent () dans l'espoir que fixerait mon problème de celui-ci ne s'appeler (même à (repeindre)), mais pas de chance.

package jpanelpaint;

import java.awt.*;
import javax.imageio.*;
import javax.swing.*;
import java.io.*;
import java.util.ArrayList;

public class ImageLoadTest extends JComponent {
 ArrayList<Image> list;

 public ImageLoadTest() {
  list = new ArrayList<Image>();

  try { //create the images (a deck of 4 cards)
   for(String name : createImageFileNames(4)){
    System.err.println(name);
    list.add(ImageIO.read(new File(name)));
   }
  } catch (IOException e) {  }
 }

    protected void paintComponent(Graphics g) {
     int yOffset=0;
  System.err.println("ImageLoadTest.paintComponent()");
     for(Image img : list) {
      g.drawImage(img, 0, yOffset,  null);
      yOffset+=20;
     }
    }

 public static void main(String args[]) throws InterruptedException {
  JFrame frame = new JFrame("Empty JFrame");
  frame.setSize(new Dimension(1000, 500));
  frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

  frame.setVisible(true);

  Thread.sleep(1000);
  frame.setTitle("Loading images");
  ImageLoadTest ilt = new ImageLoadTest();
  frame.add(ilt);
  //update the screen
  //DOESN'T WORK. only works if I call frame.paintAll(frame.getGraphics()) 
  ilt.repaint();
  frame.repaint();

  Thread.sleep(1000);
  frame.setTitle("Setting background");
  ilt.setBackground(Color.BLACK);
  //update the screen - DOESN'T WORK even if I call paintAll ..
  ilt.repaint();
  frame.repaint();

            //have to call one of these to get anything to display  
//  ilt.paintComponent(frame.getGraphics()); //works
  frame.paintComponents(frame.getGraphics()); //works
 }

 //PRIVATE HELPER FUNCTIONS

 private String[] createImageFileNames(int count){
  String[] fileNames = new String[count];
  for(int i=0; i < count; i++)
   fileNames[i] = "Cards" + File.separator + (i+1) + ".bmp";  
  return fileNames;
 }
}
Était-ce utile?

La solution 4

Ce sont les principaux problèmes avec le code original qui a causé de ne pas travailler:

  1. pas le fonctionnement appeler validate () après un ajout ()
  2. ne pas fixer la taille préférée du composant.
  3. ne pas appeler super.paintComponent () lors de la substitution (cela fait la setBackground () a fonctionne pas)
  4. je devais hériter de JPanel afin qu'il se peint. Ni le composant ni JComponent était suffisante pour le setBackground () pour travailler, même lorsque le point de fixation 3.

Après avoir fait ce qui précède, il n'a pas vraiment d'importance si vous appelez la méthode paintComponent ou de la peinture, les deux semblaient travailler aussi longtemps que je me suis souvenu d'appeler le constructeur super au début.

Cette information a été assemblé à partir de ce @jitter, @tackline et @camickr écrit, si grand bravo!

P.S. Aucune idée si répondre à votre propre question est considérée comme mauvaise forme, mais étant donné que les informations dont je avais besoin a été assemblé à partir de plusieurs réponses, je pensais que la meilleure façon était upmodding les autres réponses et écrire une somme comme ça.

Autres conseils

L'une des raisons pour lesquelles le paintComponent () ne soit pas invoqué dans le code d'origine est parce que le composant a une « taille zéro » et le RepaintManger est assez intelligent pour ne pas essayer de peindre quelque chose sans avoir.

La raison pour laquelle la remise en ordre du code fonctionne est parce que lorsque vous ajoutez le composant au châssis, puis faites le cadre visible le gestionnaire de mise en page est invoquée pour la mise en page du composant. Par défaut, un cadre utilise un BorderLayout et par défaut d'un composant est ajouté au centre du BorderLayout qui se donne tout l'espace disponible pour le composant de sorte qu'il se peint.

Cependant, vous changez le volet gestionnaire du contenu de mise en page pour un FlowLayout, vous auriez encore un problème parce qu'un FlowLayout respecte la taille préférée du composant qui est égal à zéro.

Alors, que vous avez vraiment besoin de lui affecter une taille préférée pour vous votre composant afin que les gestionnaires de mise en page peuvent faire leur travail.

Un problème majeur ici est que vous ne modifiez pas vos composants swing sur le discussion de répartition des événements (HAE) . Essayez d'envelopper tout le code dans votre principale méthode dans ce qui suit:

    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            // swing code here...             
        }
    });

Aussi: ajoutez votre ImageLoadTest au cadre avant de fixer le cadre visible. Ceci est basé sur une lecture superficielle rapide du code -. Je vais le lire plus loin et voir ce que je peux trouver

EDIT:

Suivez mon conseil d'origine ci-dessus, et de simplifier votre principale méthode pour ressembler à ce qui suit et votre paintComponent () sera appelé:

public static void main(String args[]) throws InterruptedException {
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            JFrame frame = new JFrame("Empty JFrame");
            frame.setSize(new Dimension(1000, 500));
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            PaintComponentTest ilt = new PaintComponentTest();
            frame.add(ilt);
            frame.setVisible(true);
            ilt.setBackground(Color.BLACK);
        }
    });
}

Aussi je lirais sur l'utilisation des minuteries pour effectuer l'animation, ainsi que l'événement général Swing dispatching et comment / quand remplacer diverses méthodes de peinture.

http://java.sun.com/products/jfc/ tsc / articles / peinture /

http://java.sun.com/docs /books/tutorial/uiswing/misc/timer.html

http://java.sun.com/docs /books/tutorial/uiswing/concurrency/dispatch.html

Pour faire Tom Hawtin - tackline heureux. Je réécris encore une fois

Il y a plusieurs choses que je changé (vérifier les lignes avec le commentaire //new)

Réécriture complètement

  • de Split dans un fichier nouveau composant propre (ImageLoadTest.java) et un fichier pour le tester (Tester.java)

Améliorations sur le code d'affiches originales

  • appeler constructeur de parent dans le constructeur de ImageLoadTest (super())
  • fourni deuxième constructeur pour définir la liste des images où le composant doit afficher
  • IMPORTANT: appel à setPreferredSize() du composant dans le constructeur. Si la taille n'est pas définie swing cours ne sera pas la peinture de votre composant. taille préférée est basée sur max. largeur de toutes les images et le total de toutes les hauteurs d'image
  • appel à super.paintComponent(g) dans paintComponent() surchargée
  • paintComponent modifié à base de yOffset automatiquement sur la hauteur des images en cours d'élaboration

  • initialisation de l'interface graphique fait sur EDT

  • code original basé sur l'utilisation sleep() pour illustrer le chargement et le chargement des images peut prendre beaucoup de temps SwingWorker sont utilisés de
  • worker attend en définit alors nouveau titre et charge ensuite les images
  • à la fin de la worker dans done() ajoute enfin le composant à l'JFrame et l'affiche. composant ajouté au volet Contenu de JFrame comme décrit dans DFrame api. Et comme décrit dans javadoc fait appel nécessaire pour validate() sur JFrame après avoir appelé add(), comme JFrame est un conteneur déjà visible whichs enfants ont changé.
  

citation de javdoc de validate()

     

Le procédé de validation est utilisé pour provoquer une   récipient pour Étalez ses sous-composants   encore. Il doit être invoqué lorsque cette   Les sous-composants de conteneurs sont modifiés   (Ajouté ou enlevé de la   récipient, ou liés à la mise en page   informations modifiées) après la   conteneur a été affiché.

  • deuxième travailleur ne vient un peu plus d'attente définit ensuite la couleur d'arrière-plan noir
  • utilisé JPanel comme baseclass pour ImageLoadTest de fixer setBackground() que je ne pouvais pas travailler avec JComponent.

Ainsi, vos principaux problèmes où que vous n'a pas fixé la taille préférée du composant et que vous n'avez pas appelé validate() sur le JFrame après avoir ajouté quelque chose au conteneur déjà visible.

Cela devrait fonctionner

jpanelpaint / ImageLoadTest.java

package jpanelpaint;

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.JPanel;
import java.util.List;

public class ImageLoadTest extends JPanel {
  private List<Image> list;

  public ImageLoadTest() {
    super();
  }

  public ImageLoadTest(List<Image> list) {
    this();
    this.list = list;
    int height = 0;
    int width = 0;
    for (Image img : list) {
      height += img.getHeight(this);
      width = img.getWidth(this) > width ? img.getWidth(this) : width;
      setPreferredSize(new Dimension(width, height));
    }
  }

  @Override
  protected void paintComponent(Graphics g) {
    int yOffset=0;
    super.paintComponent(g);
    System.err.println("ImageLoadTest.paintComponent()");
    for(Image img : list) {
      g.drawImage(img, 0, yOffset, null);
      yOffset+=img.getHeight(this);
    }
  }
}

Tester.java

import java.awt.Dimension;
import java.awt.Color;
import java.awt.Image;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.SwingWorker;
import javax.swing.SwingUtilities;
import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import jpanelpaint.ImageLoadTest;

public class Tester {

  private JFrame frame;
  private ImageLoadTest ilt;
  private final int NUMBEROFFILES = 4;
  private List<Image> list;

  //will load the images
  SwingWorker worker = new SwingWorker<List<Image>, Void>() {
    @Override
    public List<Image> doInBackground() throws InterruptedException {
      //sleep at start so user is able to see empty jframe
      Thread.sleep(1000);
      //let Event-Dispatch-Thread (EDT) handle this
      SwingUtilities.invokeLater(new Runnable() {
        public void run() {
          frame.setTitle("Loading images");
        }
      });
      //sleep again so user is able to see loading has started
      Thread.sleep(1000);
      //loads the images and returns list<image>
      return loadImages();
    }

    @Override
    public void done() {
      //this is run on the EDT anyway
      try {
        //get result from doInBackground
        list = get();
        frame.setTitle("Done loading images");
        ilt = new ImageLoadTest(list);
        frame.getContentPane().add(ilt);
        frame.getContentPane().validate();
        //start second worker of background stuff
        worker2.execute();
      } catch (InterruptedException ignore) {}
      catch (ExecutionException e) {
        String why = null;
        Throwable cause = e.getCause();
        if (cause != null) {
          why = cause.getMessage();
        } else {
          why = e.getMessage();
        }
        System.err.println("Error retrieving file: " + why);
      }
    }
  };

  //just delay a little then set background
  SwingWorker worker2 = new SwingWorker<Object, Void>() {
    @Override
    public List<Image> doInBackground() throws InterruptedException {
      Thread.sleep(1000);
      SwingUtilities.invokeLater(new Runnable() {
        public void run() {
          frame.setTitle("Setting background");
        }
      });
      Thread.sleep(1000);
      return null;
    }

    @Override
    public void done() {
      ilt.setBackground(Color.BLACK);
      frame.setTitle("Done!");
    }
  };

  public static void main(String args[]) {
    new Tester();
  }

  public Tester() {
    //setupGUI
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        frame = new JFrame("Empty JFrame");
        frame.setSize(new Dimension(1000, 500));
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
      }
    });

    //start the swingworker which loads the images
    worker.execute();
  }

  //create image names
  private String[] createImageFileNames(int count){
    String[] fileNames = new String[count];
    for(int i=0; i < count; i++)
      fileNames[i] = "Cards" + File.separator + (i+1) + ".bmp"; 
    return fileNames;
  }

  //load images
  private List<Image> loadImages() {
    List<Image> tmpA = new ArrayList<Image>();
    try {
      for(String name : createImageFileNames(NUMBEROFFILES)){
        System.err.println(name);
        tmpA.add(ImageIO.read(new File(name)));
      }
    } catch (IOException e) { }

    return tmpA;
  }
}

Je recommande la lecture des deux premiers chapitres de « clients Filthy Rich ». J'avais utilisé swing pendant des années, mais seulement après avoir lu ce livre, je ne comprends enfin pleinement exactement comment le mécanisme de peinture de Java fonctionne.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top