# Fragen zu Swing, paintComponent() und repaint



## demian (4. Dez 2009)

Hallo liebes Forum,

ich arbeite mich derzeit in Swing und AWT ein und mir sind zwei Dinge grundsätzlich unklar. Es wäre super, wenn mir jemand helfen könnte und die Unklarheiten beseitigen könnte. Anbei ein Codeschnipsel aus dem Buch "Handbuch der Java Programmierung" (Listing3409), das ich deutlich reduziert und auf Swing umgestellt (JFrame statt Frame) habe. Ich hoffe, ihr verzeiht mir, dass das hier sicher nicht als Minimalbeispiel durchgeht, aber ich kann so am besten mein Problem erklären.

[Java] package code;
/*basiert auf Listing3409.java aus Handbuch der Java-Programmierung */
import java.awt.*;
import java.util.*;
import javax.swing.JFrame;

public class Minimalbeispiel
extends JFrame
implements Runnable
{
  //Konstanten
  private static final int   SLEEP       = 200;

  //Instanzvariablen
  private Thread th;
  private Vector snake;
  protected int dx;
  protected int dy;

  public static void main(String[] args)
  {
    Minimalbeispiel frame = new Minimalbeispiel();
    Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
    frame.setSize(d);
    frame.setVisible(true);
    frame.startAnimation();

  }

  public Minimalbeispiel()
  {
    super("Animierte Schlange");
    setBackground(Color.LIGHT_GRAY);
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    snake = new Vector();
  }

  public void startAnimation()
  {
    th = new Thread(this);
    th.start();
  }

  public void run()
  {
    //Schlange konstruieren
    Rectangle cr;
    int x = 100;
    int y = 100;
    for (int i=0; i < 5; ++i) {
      cr = new Rectangle(x, y, 20, 20);
      x += 20;
      snake.addElement(cr);
    }

    //Vorzugsrichtung festlegen
    dx = 1;
    dy = 0;

    //Schlange laufen lassen
    while (true) {
      try {
        Thread.sleep(SLEEP);
      } catch (InterruptedException e){
        //nichts
      }
      moveSnake();
//Was macht hier repaint()? Scheinbar nicht alles neu zeichnen, was passiert, wenn ich die Fenstergröße ändere
      repaint();
    }
  }

  public void moveSnake()
  {
    Dimension size = getSize();
    int sizex = size.width-getInsets().left-getInsets().right;
    int sizey = size.height-getInsets().top-getInsets().bottom;
    Rectangle cr = (Rectangle)snake.firstElement();
    int xalt, yalt;
    int xtmp, ytmp;

    xalt = cr.x + 20 * dx;
    yalt = cr.y + 20 * dy;
    //Rest der Schlange hinterherziehen
    Enumeration e = snake.elements();
    while (e.hasMoreElements()) {
      cr = (Rectangle)e.nextElement();
      xtmp = cr.x;
      ytmp = cr.y;
      cr.x = xalt;
      cr.y = yalt;
      xalt = xtmp;
      yalt = ytmp;
    }
  }
  //public void paintComponent(Graphics g) funktioniert nicht
  public void paint(Graphics g)
  {
    Rectangle cr;
    Enumeration e = snake.elements();
    while (e.hasMoreElements()) {
      cr = (Rectangle)e.nextElement();
      g.setColor(Color.black);
      g.fillRect(cr.x,cr.y,cr.width,cr.height);
    }
  }

}[/Java]

Wenn ihr das Beispiel kompiliert, sollte sich eine Schlange von links nach rechts auf den Weg machen. Zwei Verständnisfragen nun: Wenn man die Fenstergröße ändert, wird die Schlange auf ihre ursprüngliche Größe reduziert, ansonsten wächst sie. Ich kann mir das nur so erklären: Fenster ändert sich ==> System-triggered Event ==> Paint() wird aufgerufen ==> Schlange wird neu gezeichnet und Schlange ist nunmal nur 5 Rechtecke groß. Tut man nichts, wächst die Schlange für den Betrachter einfach deshalb, weil die alten Rechtecke nicht gelöscht werden.

Falls ich das aber richtig verstehe, was macht dann repaint in der run-Methode (Zeile 69) ? Sollte repaint() nicht auch paint aufrufen und damit die Schlange neu zeichnen (damit ich eben eine immer gleich große Schlange und keine wachsende habe).

Meine zweite Frage bezieht sich auf paint(): Dieser Link von Sun (Painting in AWT and Swing) empfiehlt, nie paint zu überschreiben, stattdessen nur paintComponent(). Das klingt dort so, als ob es immer vollkommen ausreichend sei, nur paintComponent zu überschreiben. Mache ich das aber hier (Zeile 97), tut sich einfach nichts. Warum? Wo liegt mein Denkfehler? 

Verzeiht die langen Ausführungen zu später Stunde. Trotzdem hoffe ich auf Eure Hilfe.

Grüße Christoph


----------



## Marco13 (4. Dez 2009)

Verzeih' meine kurze Aufwührung wegen der späten Stunde  : Ein JFrame hat kein paintComponent - dass diese Methode nicht einfach aufgerufen wird, wenn du sie dort einbaust, ist klar. Man sollte aber auch die paint-Methode eines JFrames nicht überschreiben. Wenn man selbst etwas zeichnen will, sollte man von JPanel erben, dort paintComponent überschreiben (und die erste Zeile sollte dort [c]super.paintComponent(g);[/c] sein, und dieses Panel dann in's ContentPane vom JFrame legen. 
Und noch ganz kurz: "repaint" ruft nicht "paint" auf. "repaint" ist nur ein "Anstoßen" - ein Hinweis nach dem Motto: "Zeichne mal so bald wie möglich neu".


----------



## javimka (5. Dez 2009)

Es ist durchaus legitim auch das paint zu überschreiben, wenn das Sinn macht. Z.B. wenn mann die gesamte Komponente transparent zeichnen möchte, kann man das machen. Meistens ist das aber unnötig und man überschreibt deshalb paintComponent. Der Grund, paintComponent() zeichnet den Inhalt einer Komponente. paint() ruft der Reihe nach paintComponent(), paintBorder() und paintChildren auf. Überschreibt man dies unvorsichtig, werden keine Border und keine Kinderkomponenten mehr gezeichnet.


----------



## Marco13 (5. Dez 2009)

Ja, das stimmt (so viel zum Thema Fragestellerspezifische Antworten :bae: ), zumindest für Components wie das JPanel, aber bei einem JFrame paint zu überschreiben ist schon ein bißchen "heikel"... man sollte sich zumindest mal ansehen, was DER in der paint so alles macht...


----------



## javimka (5. Dez 2009)

demian hat gesagt.:


> Meine zweite Frage bezieht sich auf paint(): Dieser Link von Sun (Painting in AWT and Swing) empfiehlt, nie paint zu überschreiben, stattdessen nur paintComponent(). Das klingt dort so, als ob es immer vollkommen ausreichend sei, nur paintComponent zu überschreiben.


Manchmal sind die Fragen zwischen den zeilen geschreiben ueh:


----------



## André Uhres (5. Dez 2009)

Versuch's mal so:

```
import java.awt.*;
import java.util.*;
import javax.swing.*;

public class Minimalbeispiel extends JFrame implements Runnable {

    private static final int SLEEP = 200;
    private Thread th;
    private Vector snake;
    protected int dx;
    protected int dy;
    private final MyPanel myPanel;

    public Minimalbeispiel() {
        super("Animierte Schlange");
        setBackground(Color.LIGHT_GRAY);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        snake = new Vector();
        setSize(600, 300);
        setLocationRelativeTo(null);
        myPanel = new MyPanel(snake);
        add(myPanel);
        startAnimation();
    }

    public void startAnimation() {
        th = new Thread(this);
        th.start();
    }

    public void run() {
        //Schlange konstruieren
        Rectangle cr;
        int x = 100;
        int y = 100;
        for (int i = 0; i < 5; ++i) {
            cr = new Rectangle(x, y, 20, 20);
            x += 20;
            snake.addElement(cr);
        }
        //Vorzugsrichtung festlegen
        dx = 1;
        dy = 0;
        //Schlange laufen lassen
        while (true) {
            try {
                Thread.sleep(SLEEP);
            } catch (InterruptedException e) {
                //nichts
            }
            moveSnake();
        }
    }

    public void moveSnake() {
        Rectangle cr = (Rectangle) snake.firstElement();
        int xalt, yalt;
        int xtmp, ytmp;
        xalt = cr.x + 20 * dx;
        yalt = cr.y + 20 * dy;
        //Rest der Schlange hinterherziehen
        Enumeration e = snake.elements();
        while (e.hasMoreElements()) {
            if (xalt > myPanel.getWidth()) {
                dx = -1;
            } else if (xalt < 0) {
                dx = 1;
            }
            cr = (Rectangle) e.nextElement();
            xtmp = cr.x;
            ytmp = cr.y;
            //
            int OFFSET = 20;
            myPanel.repaint(cr.x - OFFSET, cr.y - OFFSET,
                    cr.width + OFFSET * 2, cr.height + OFFSET * 2);
            cr.x = xalt;
            cr.y = yalt;
            myPanel.repaint(cr.x - OFFSET, cr.y - OFFSET,
                    cr.width + OFFSET * 2, cr.height + OFFSET * 2);
            //
            xalt = xtmp;
            yalt = ytmp;
        }
    }

    public static void main(String[] args) {
        Runnable gui = new Runnable() {

            public void run() {
                new Minimalbeispiel().setVisible(true);
            }
        };
        SwingUtilities.invokeLater(gui);

    }
}

class MyPanel extends JPanel {

    private final Vector snake;

    MyPanel(Vector snake) {
        this.snake = snake;

    }

    @Override
    protected void paintComponent(final Graphics g) {
        super.paintComponent(g);
        Rectangle cr;
        Enumeration e = snake.elements();
        while (e.hasMoreElements()) {
            cr = (Rectangle) e.nextElement();
            g.setColor(Color.black);
            g.fillRect(cr.x, cr.y, cr.width, cr.height);
        }
    }
}
```
Siehe auch: 
Malen in Swing Teil 1: der grundlegende Mechanismus - Byte-Welt Wiki
Lesson: Performing Custom Painting (The Java™ Tutorials > Creating a GUI With JFC/Swing)
Tipp: wenn du eine Methode überschreiben willst, benutze die @Override Annotation, dann sagt der Compiler dir Bescheid, falls die Methode in deiner Klasse nicht überschrieben/implementiert werden kann.


----------



## demian (5. Dez 2009)

Hallo Zusammen, 

erstmal vielen Dank für Eure Hilfe. Jetzt ist mir wieder alles klar: Ich habe ja in dem Swing-Tutorial von Sun gelesen, dass JFrame ein Container und keine JComponent ist. Wahrscheinlich macht deswegen dort paintComponent() keinen Sinn. Sehr gut nun zu wissen, da grundsätzlich noch ein JPanel darüber zu legen und das zum JFrame hinzuzufügen. 

Und André, vielen Dank für dein angepasstes Beispiel. Es arbeitet jetzt so, wie ich es anfangs wollte, und ich kann es nun am Code nachvollziehen. Super! Ich werde mir auch gleich deine Links durchlesen.

Schönen Abend Euch noch.

Grüße Christoph


----------

