Domanda

Per gli ultimi due giorni ho cercato di capire come Java gestisce la grafica, ma hanno fallito miseramente a questo.Il mio problema principale è capire esattamente come e quando paint() (o il più recente paintComponent() ) è/dovrebbe essere chiamato.

Nel codice riportato di seguito ho fatto vedere quando le cose sono state create, il paintComponent() non viene mai chiamato, a meno di non aggiungere manualmente una chiamata a io o chiamate al JFrame.paintAll()/JFrame.paintComponents().

Ho rinominato il paint() metodo paintComponent (), nella speranza che sarebbe risolvere il mio problema di non essere mai chiamato (anche a repaint()), ma senza fortuna.

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;
 }
}
È stato utile?

Soluzione 4

Questi sono stati i principali problemi con il codice originale che ha causato non funzionare:

  1. non chiamare validate () dopo un'operazione add ()
  2. non impostare la dimensione preferita del componente.
  3. non chiamare super.paintComponent () quando si esegue l'override di esso (questo ha reso la setBackground () chiamata non funziona)
  4. avevo bisogno di ereditare da JPanel in modo che esso per ottenere verniciato. Né Componente né JComponent era sufficiente per il setBackground () chiamata a funzionare, anche se fissare il punto 3.

Dopo aver fatto quanto sopra, in realtà non aveva importanza se si chiama il metodo paintComponent o vernice, entrambi sembrava funzionare fino a quando mi sono ricordato di chiamare il costruttore eccellente alla partenza.

Questa informazione è stata assemblata da quello che @jitter, @tackline, e @camickr scrisse, così grandi complimenti!

P.S. Non ho idea se rispondere alla tua domanda è considerata di cattivo gusto, ma dato che le informazioni di cui avevo bisogno è stato assemblato da più risposte, ho pensato che il modo migliore è stata upmodding le altre risposte e la scrittura di una somma in questo modo.

Altri suggerimenti

Una delle ragioni del paintComponent () non viene invocata nel codice originale è perché il componente ha un "size zero" e la RepaintManger è abbastanza intelligente da non provare a dipingere qualcosa senza dimensioni.

La ragione il riordino del codice funziona è perché quando si aggiunge il componente al telaio e quindi effettuare la cornice visibile il gestore di layout viene richiamato al layout del componente. Per default un telaio utilizza un BorderLayout e di default un componente viene aggiunto al centro della BorderLayout che avviene dare tutto lo spazio disponibile per il componente in modo che viene verniciato.

Tuttavia, è cambiare il gestore di layout del riquadro del contenuto di essere un FlowLayout, si avrebbe ancora un problema, perché un FlowLayout rispetta la dimensione preferita del componente, che è pari a zero.

Quindi quello che ha realmente bisogno di fare è assegnare una dimensione preferita a voi il componente in modo da gestori di layout possono fare il loro lavoro.

Una questione importante qui è che non sta aggiornando i componenti swing sul evento spedizione Discussione (EDT) . Provare avvolgendo tutto il codice nel metodo principale di quanto segue:

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

Inoltre: aggiungi la tua ImageLoadTest al telaio prima di impostare la cornice a vista. Questo si basa su una lettura superficiale rapida del codice -. Leggerò ulteriormente e vedere cos'altro posso trovare

Modifica

Seguite il mio consiglio originale al di sopra, e semplificare il metodo principale per simile al seguente e il vostro paintComponent () sarà chiamata:

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);
        }
    });
}

Inoltre vorrei leggere su utilizzando i timer per eseguire l'animazione, così come evento generale swing dispacciamento e come / quando eseguire l'override vari metodi di vernice.

http://java.sun.com/products/jfc/ TSC / articoli / pittura /

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

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

Per rendere Tom Hawtin - tackline felice.Ho riscritto ancora una volta

Ci sono molte cose che ho cambiato (controllare le linee con le //new commento)

Riscritto completamente

  • Split in un ambiente pulito nuovo file di componente (ImageLoadTest.java) e un file di prova (Tester.java)

Miglioramenti su manifesti originali codice

  • chiamare il costruttore di padre in ImageLoadTest costruttore (super())
  • secondo costruttore di impostare un elenco delle immagini componente che dovrebbe visualizzare
  • IMPORTANTE:chiamata a setPreferredSize() di componente del costruttore.Se la dimensione non è impostato swing, naturalmente, non dipingere il vostro componente.dimensione preferita è basato su max.la larghezza di tutte le immagini e sulla somma di tutte le immagini heights
  • chiamata a super.paintComponent(g) in ignorate paintComponent()
  • cambiato paintComponent automaticamente base yOffset all'altezza di immagini tratte

  • GUI inizializzazione fatto su EDT

  • come codice originale, basato sull'uso di sleep() per illustrare il caricamento e il caricamento delle immagini potrebbe richiedere molto tempo SwingWorker's sono utilizzati
  • worker attende quindi che stabilisce il nuovo titolo e poi carichi le immagini
  • al termine del worker in done() infine aggiunge la componente JFrame e la mostra.Componente aggiunto al riquadro del contenuto di JFrame come descritto in JFrame api.E, come descritto nel javadoc reso necessario chiamare a validate() su JFrame dopo la chiamata add(), come il JFrame è già visibile contenitore whichs bambini cambiata.

javdoc citazione validate()

Il metodo validate è utilizzato a causa di un contenitore per porre le sue sottocomponenti di nuovo.Dovrebbe essere invocato quando questo contenitore sottocomponenti sono modificati (aggiunto o rimosso dal contenitore, o relative al layout informazioni cambiato) dopo il il contenitore è stato visualizzato.

  • secondo lavoratore appena fa un po ' di più in attesa quindi imposta il colore di sfondo nero
  • usato JPanel come baseclass per ImageLoadTest per risolvere setBackground() che non potevo andare al lavoro con JComponent.

Così i vostri problemi principali in cui che non hai impostato la dimensione preferita del componente e che non hai chiamata validate() sul JFrame dopo l'aggiunta di qualcosa di già visibili contenitore.

Questo dovrebbe funzionare

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;
  }
}

vi consiglio di leggere i primi due capitoli di "Clienti Filthy Rich". Avevo usato Altalena per anni, ma solo dopo aver letto questo libro fatto finalmente comprendere appieno esattamente come funziona il meccanismo pittura di Java.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top