# Warum macht die einfache Animation einen kleinen Fehler?



## ernst (22. Aug 2012)

Hallo allerseits,
1)
in dem einfachen Demo-Programm (siehe unten) bewegt sich ein Rechteck immer wieder von links nach rechts. 
Wenn das Rechteck das 1.Mal rechts ankommt bewegt es sich wieder von links.
Aber dann befindet sich plötzlich ein kleiner roter Strich auf der linken Seite.
Warum ist das so ? (wenn man das Fenster verändert, z.B. vergrößert geht der rote Strich wieder weg).

2) Bis jetzt dachte ich, dass repaint bewirkt, dass das Bild neu gezeichnet wird.
Wenn das stimmt, warum muß dann in dem Link 
http://www.java-forum.org/java-basics-anfaenger-themen/135969-anzeige-erneuern-geht.html
der alte Zeiger der Uhr mit
g.clearRect(0, 0, getWidth(), getHeight()); 
gelöscht werden.
Die alte Zeichnung ist doch uninteressant, da sowieso neu gezeichnet wird.

mfg
Ernst


```
package demoanimation10;

import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.Timer;

public class MainDemoAnimation10 {

    public static void main(String[] args) {
        AnimationView animationView = new AnimationView();
    }
}

class TimerActionListener implements ActionListener {
    private AnimationJPanel animationJPanel;
    
    public TimerActionListener(AnimationJPanel animationJPanel){
        this.animationJPanel=animationJPanel;
    }
    
    // Wird nach jedem Feuern des Timers automatisch aufgerufen
    public void actionPerformed(ActionEvent ae) {
        // Daten updaten
        int x;
        x=animationJPanel.getX();
        if(x <= 400){
            x = x + 1;
        }
        else{
            x=0;
        }
        animationJPanel.setX(x);
        // neu zeichnen
        animationJPanel.repaint();                
    }
}

class AnimationView extends javax.swing.JFrame{
   
    public AnimationView() {
	// Stelle in MyFenster deklarieren, an die Buttons, Panels, usw.
        // montiert werden.
        Container mycont;  
        // Ein Panel (Zeichenfläche), das an mycont montiert wird.
        AnimationJPanel animationJPanel;        
        // Ein Timer zum feuern.
        Timer timer;        
        // Liefert die Stelle, an die montiert wird.
        mycont = getContentPane();                
        // festlegen der Breite und Höhe des Fensters 
        this.setSize(500,500);
        animationJPanel = new AnimationJPanel();
        // Montieren des Panels (Zeichenfläche) an mycont 
        mycont.add(animationJPanel);
        // Fensterüberschrift festlegen    
	setTitle("Meine erste Animation");
        // Es wird ein Objekt eines ActionListener (besser. einer Unterklasse),
        // d.h. einer Wanze erzeugt.
        TimerActionListener tal=new TimerActionListener(animationJPanel);        
        // Dann wird ein Timer erstellt und das Objekt dieses ActionListeners 
        // dabei an diesem Timer registriert (angebracht).
        // Nach einer gewissen Zeit feuert der Timer immer wieder ein Objekt 
        // der Klasse ActionEvent ab, das automatisch der entsprechenden 
        // Methode actionPerformed(...) des ActionListeners übergeben wird.
        timer = new Timer(2,tal);
        // Dann wird dieser Timer gestartet.        
        timer.start();
        // macht das Fenster sichtbar    
        this.setVisible(true);        
        // Programm wird beendet (aus dem Arbeitsspeicher entfernt), wenn 
        // Fenster weggeklickt wird. Nachprüfen mit Task-Manager
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

class AnimationJPanel extends javax.swing.JPanel {
    // Daten für Methode paintComponent() ---
    private int x;
    private int y;

    public int getX(){
        return x;
    }
    
    public void setX(int x){
        this.x=x;
    }
    
    public int getY(){
        return y;
    }

    public void setY(int y){
        this.y=y;
    }
  
    public AnimationJPanel() {
        x=0;
        y=0;
    }
    
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(Color.red);
        g.fillRect(x, y, 30, 25);
    }
}
```


----------



## tribalup (22. Aug 2012)

Bei mir bleibt es rechts stehen und dann dauerts 3 Sekunden bis es links wieder kommt. Ein roter Strich ist bei mir nicht.


----------



## KingOfExceptions (22. Aug 2012)

Bei mir ist auch kein roter Strich. Aber es kommt bei mir bei anderen Programmen auch manchmal vor, dass bei großen Bildern manchmal kurzzeitig Fehler auftreten. Das passiert vor Allem beim often und schnellen resizen von einem JFrame. Das liegt aber denke ich mal an deinem/unseren PC/PCs und der Grafikkarte, dem Prozessor oder so...


----------



## ernst (22. Aug 2012)

KingOfExceptions hat gesagt.:


> Bei mir ist auch kein roter Strich. Aber es kommt bei mir bei anderen Programmen auch manchmal vor, dass bei großen Bildern manchmal kurzzeitig Fehler auftreten. Das passiert vor Allem beim often und schnellen resizen von einem JFrame. Das liegt aber denke ich mal an deinem/unseren PC/PCs und der Grafikkarte, dem Prozessor oder so...



Danke für eure Tests.
Aber was ist mit der 2. Frage:

2) Bis jetzt dachte ich, dass repaint bewirkt, dass das Bild neu gezeichnet wird.
Wenn das stimmt, warum muß dann in dem Link 
Java programmieren aus Leidenschaft
der alte Zeiger der Uhr mit
g.clearRect(0, 0, getWidth(), getHeight()); 
gelöscht werden.
Die alte Zeichnung ist doch uninteressant, da sowieso neu gezeichnet wird.

mfg
Ernst


----------



## KingOfExceptions (22. Aug 2012)

Zu der 2.Frage:

In dem Thread macht der TO etwas ein bisschen anders:

Es gibt mehrere Möglichkeiten, um zu verhindern, dass die alten Komponenten bei einem repaint() noch sichtbar sind, hier mal zwei davon:
1-man benutzt 
	
	
	
	





```
g.clearRect(...);
```
 und überdeckt das Vorhandene mit einer (weißen) Fläche
2-man benutzt den 
	
	
	
	





```
super
```
-Aufruf, d.h den Konstruktor der Superklass (
	
	
	
	





```
extends JPanel
```
)

Würde man das nicht machen, dann sähe das JFrame ein wenig komisch aus  
Oder bei einem sich bewegenden Quadrat, siehe Bild.
IMHO ist die 2. Möglichkeit die beste, jedoch muss man dann das 'Feature' 
	
	
	
	





```
extends
```
 für JPanel verwenden. 
Für mehr Infos: [JAPI]http://docs.oracle.com/javase/1.4.2/docs/api/java/awt/Graphics.html[/JAPI]


----------



## ernst (23. Aug 2012)

KingOfExceptions hat gesagt.:


> Zu der 2.Frage:
> 
> In dem Thread macht der TO etwas ein bisschen anders:
> 
> ...



Danke für die Infos.

Nochmals zu meiner 1. Frage zurück.
Wenn ich im Demo-Programm unten _mehrmals_ auf den Button "schneller" drücke, wird der rote Strich links immer größer. Ist das bei euch auch der Fall ? Wenn ja, warum ?

mfg
Ernst


```
package wagenanimationmvc15;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;

class AnimationController {
    private AnimationView animationView;
    private AnimationModel animationModel;
    
    public AnimationController(int viewlaenge, int viewbreite, int strassenlaenge, int strassenbreite) {
        animationModel = new AnimationModel(strassenlaenge, strassenbreite);        
        animationView = new AnimationView(animationModel, viewlaenge, viewbreite);
        addListener();
    }
    
    
    public AnimationView getAnimationView() {
        return animationView;
    }
    
    public void addListener(){
        B1_ActionListener b1_ActionListener;
        b1_ActionListener= new B1_ActionListener(animationModel);
        animationView.set_b1_Listener(b1_ActionListener);
        Timer timer;        
        TimerActionListener tal;
        tal=new TimerActionListener(animationModel);
        timer = new Timer(2,tal);
        timer.start();
        
    }
    

}

class TimerActionListener implements ActionListener {
    private AnimationModel animationModel;    

    public TimerActionListener(AnimationModel animationModel){
        this.animationModel=animationModel;
    }
    
    public void actionPerformed(ActionEvent ae) {
        animationModel.updatenTimer();
    }
}


class B1_ActionListener implements ActionListener {
    private AnimationModel animationModel;    

    public B1_ActionListener(AnimationModel animationModel){
        this.animationModel=animationModel;
    }
    
    public void actionPerformed(ActionEvent ae) {
        animationModel.updatenButton_B1();
    }
}
```


```
package wagenanimationmvc15;
import java.awt.Color;
import java.awt.Graphics;

class AnimationJPanel extends javax.swing.JPanel {
    // Daten für Methode paintComponent() ---
    private int x;
    private int y;

    public int getX(){
        return x;
    }
    
    public void setX(int x){
        this.x=x;
    }
    
    public int getY(){
        return y;
    }

    public void setY(int y){
        this.y=y;
    }
  
    public AnimationJPanel() {
    }
    
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(Color.red);
        g.fillRect(x, y, 30, 25);
    }
}
```


```
package wagenanimationmvc15;

import java.util.Observable;

class AnimationModel extends Observable{
    private Wagen wagen;
    private int strassenlaenge;
    private int strassenbreite;    

    public Wagen getWagen(){
        return wagen;
    }
    
    public void updatenTimer() {
        wagen.fahren();
        setChanged();
        notifyObservers();        
    }

    public void updatenButton_B1() {
        wagen.veraendereSchritt(1);
        setChanged();
        notifyObservers();        
    }
    
    
    public AnimationModel(int strassenlaenge, int strassenbreite) {
        wagen=new Wagen(strassenlaenge, strassenbreite);
    }
}
```





```
package wagenanimationmvc15;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.GridLayout;
import java.util.Observable;
import java.util.Observer;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;


/*
AnimationView ist eine überwachende Klasse. Deswegen muß sie die 
Schnittstelle Observer implementieren und unbedingt die Methode
update() ausprogrammieren. 
update() wird immer dann aufgerufen, wenn sich in einer überwachten
Klasse etwas ändert (dies muß durch die Anweisungen setchanged()
und notifyObservers() vorbereitet werden).        
*/  
class AnimationView extends javax.swing.JFrame implements Observer{
    private AnimationJPanel animationJPanel;
    private AnimationModel animationModel;
    private JButton b1;    

 
    
    public AnimationView(AnimationModel animationModel, int viewlaenge, int viewbreite) {
        JPanel buttonsJPanel;
        BorderLayout borderLayout;
        GridLayout gridLayout_1_2;
        Container mycont;        
        this.animationModel = animationModel;
        // Überwachung: Es wird ein Überwacher (=Wanze, Detektiv) an
        // dem Objekt animationModell angebracht.
        this.animationModel.addObserver(this);
        // Buttons, Panels und Layouts erstellen
        b1=new JButton("schneller");
        buttonsJPanel=new JPanel();
        animationJPanel = new AnimationJPanel();        
        gridLayout_1_2=new GridLayout(1,2);
        borderLayout=new BorderLayout ();
        mycont = getContentPane();                
        // Panels mit Layout formatieren
        mycont.setLayout(borderLayout);        
        buttonsJPanel.setLayout(gridLayout_1_2);                
        // An Panels Buttons montieren
        buttonsJPanel.add(b1);
        // Dummy-Label montieren
        buttonsJPanel.add(new JLabel());                
        // An mycont die Panels montieren
        mycont.add(animationJPanel,BorderLayout.CENTER);
        mycont.add(buttonsJPanel,BorderLayout.SOUTH);        
        this.setSize(viewlaenge, viewbreite);        
        this.setVisible(true);        
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);        
    }
    
    public AnimationJPanel getAnimationJPanel(){
        return animationJPanel;
    }
    
    public void setNewView(){
        Wagen w;
        w = animationModel.getWagen();
        animationJPanel.setX(w.getOrtX());        
        animationJPanel.setY(w.getOrtY());                
    }

    // Wenn sich im überwachten Modell Daten aändern, wird ein Event
    // ausgelöst (ein spezielles Objekt geworfen) das von der Methde
    // update() eingefangen wird. Diese Methode wird dann also aufgerufen
    public void update(Observable m, Object o) {
        if (m == animationModel) {
            setNewView();
            animationJPanel.repaint();
        }
    }
    
    public void set_b1_Listener(B1_ActionListener al) {
        b1.addActionListener(al);
    }
    
    
}
```



```
package wagenanimationmvc15;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Observable;
import java.util.Observer;
import javax.swing.*;

public class MainWagenAnimationMVC15 {

    public static void main(String[] args){
        AnimationController animationController = new AnimationController(500, 500, 400, 400);
    }
}
```



```
package wagenanimationmvc15;

class Wagen{
    private int ortX;
    private int ortY;
    private int schritt;
    private int strassenlaenge;
    private int strassenbreite;    

    public Wagen(int strassenlaenge, int strassenbreite){
        this.strassenlaenge=strassenlaenge;
        this.strassenbreite=strassenbreite;
        schritt = 1;
    }

    public void setOrtX(int pOrtX){
        ortX = pOrtX;
    }

    public int getOrtX(){
        return ortX;
    }

    public void setOrtY(int pOrtY){
        ortY = pOrtY;
    }

    public int getOrtY(){
        return ortY;
    }
    
    public void veraendereSchritt(int wert){
        schritt=schritt+wert;
    }
    
    void fahren(){
        if(ortX <= strassenlaenge){
            ortX = ortX + schritt;
        }
        else{
            ortX=0;
        }
    }

    void setSchritt(int pSchritt){
        schritt = pSchritt;
    }

}
```


----------



## Spacerat (23. Aug 2012)

Würdet ihr euch nicht gerade im Swing-API bewegen, würde ich sagen, die clearMethode sei die sicherere, weil man dann genau weis, was wann wo gecleared wird. Anders ist's wenn man dieses Clear einer Super-Implementation überlässt z.B. "super.paint()" (wobei jeder weiss, dass "super.paint()" in AWT nämlich überhaupt nichts dergleichen tut). "paintComponent()" aber ist Swing und dort auch noch die empfohlene Methode. Obwohl ich es mir kaum vorstellen kann, sieht es so aus, als ob "paintComponent()" von OS zu OS, von Version zu Version und damit im Prinzip auch von Rechner zu Rechner anders implementiert wurde. Wenn dem so ist, müsste man in Swing plötzlich beides machen; "super.paintComponent(g)" und "g.clearRect()".


----------



## tribalup (23. Aug 2012)

Spacerat hat gesagt.:


> Obwohl ich es mir kaum vorstellen kann, sieht es so aus, als ob "paintComponent()" von OS zu OS, von Version zu Version und damit im Prinzip auch von Rechner zu Rechner anders implementiert wurde.



Da hier jeder ein anderes Ergebnis beim kompilieren bekommt könnte man davon ausgehen.


----------



## KingOfExceptions (23. Aug 2012)

@ernst:

Dann mach mal, was Spacerat gesagt hat. Benutze auch 
	
	
	
	





```
g.clearRect(...);
```
 zusätzlich zu 
	
	
	
	





```
super.paintComponent(g);
```
. Vielleicht hilft das was. Ich versuche gleich mal, deinen Code auszuprobieren und das Problem, sollte es weiterhin bei mir zu sehen sein, zu lösen


----------



## Michael... (23. Aug 2012)

ernst hat gesagt.:


> Wenn ich im Demo-Programm unten


Unter Demo Programm verstehe ich etwas anderes ;-) Ist wohl etwas umfangreich geworden.


ernst hat gesagt.:


> ```
> class AnimationJPanel extends javax.swing.JPanel {
> ...
> public int getX(){
> ...


Witzbold!! Du überschreibst hier zwei wichtige Methoden von JComponent! Kein Wunder, dass der LayoutManager da Problem bekommt und Dir "graphische" Rückstände angezeigt werden.

Allgemein würde ich mir über den Klassenaufbau nochmal nachdenken. Auch die Variablenbezeichner sind nicht immer hilfreich.


----------



## ernst (23. Aug 2012)

Michael... hat gesagt.:


> Unter Demo Programm verstehe ich etwas anderes ;-) Ist wohl etwas umfangreich geworden.
> 
> Witzbold!! Du überschreibst hier zwei wichtige Methoden von JComponent! Kein Wunder, dass der LayoutManager da Problem bekommt und Dir "graphische" Rückstände angezeigt werden.
> 
> Allgemein würde ich mir über den Klassenaufbau nochmal nachdenken. Auch die Variablenbezeichner sind nicht immer hilfreich.


1)
Aaah, stimmt !! Das war der Fehler !!
Danke!

2)
>
>Allgemein würde ich mir über den Klassenaufbau nochmal nachdenken.
>
Was kann man besser machen?



>
>Auch die Variablenbezeichner sind nicht immer hilfreich.
>
Das denke ich auch.  Was kann man besser machen. Hast du einen Vorschlag?


----------



## KingOfExceptions (23. Aug 2012)

3) -b1 als Buttonnamen ist nicht super
    -tal als TimerActionListener nicht so toll
    -b1_ActionListener oder wie das war... auch nicht so toll. Wenn in den ActionListener nicht viel reinmuss, dann kannst du das auch so machen:

```
JButton button=new JButton();
button.addActionListener(new ActionListener()
{
       public void actionPerformed(Event evt)
       {
        //HIER SOLLTE NICHT SO VIEL REIN
       }
});
```


----------



## Michael... (23. Aug 2012)

z.B. sollten Methodennamen auch etwas darüber aussagen was sie machen z.B. *updatenTimer()* und *updatenButton_B1()* im Model hören sich eher danach an, als ob ein Timer oder ein Button aktualisiert werden soll, aber hier geht es ja darum das sich der Wagen bewegt bzw. die Geschwindkeit erhöht wird. moveCar() oder increaseSpeed() wären hier wohl passender. Es macht ja keinen Sinn im Methodenamen zu verpacken wer diese aufruft. (Abgesehen davon, dass man das im Voraus sowieso nicht weiß) Eventuell soll die Methode ja auch mal von einem Button B2 oder einer Taste XY oder von allen aufgerufen werden.

Auch hast Du wohl auch ein bisschen die Trennung von View und Model übertrieben, so dass Du die Positionsdaten doppelt hälst (ortX, ortY in Wagen und x,y in AnimationPanel) die Daten sollten von Model gehalten und verwaltet werden, die View wertet diese nur aus und agiert dementsprechend.


----------



## ernst (24. Aug 2012)

Michael... hat gesagt.:


> z.B. sollten Methodennamen auch etwas darüber aussagen was sie machen z.B. *updatenTimer()* und *updatenButton_B1()* im Model hören sich eher danach an, als ob ein Timer oder ein Button aktualisiert werden soll, aber hier geht es ja darum das sich der Wagen bewegt bzw. die Geschwindkeit erhöht wird. moveCar() oder increaseSpeed() wären hier wohl passender. Es macht ja keinen Sinn im Methodenamen zu verpacken wer diese aufruft. (Abgesehen davon, dass man das im Voraus sowieso nicht weiß) Eventuell soll die Methode ja auch mal von einem Button B2 oder einer Taste XY oder von allen aufgerufen werden.
> 
> Auch hast Du wohl auch ein bisschen die Trennung von View und Model übertrieben, so dass Du die Positionsdaten doppelt hälst (ortX, ortY in Wagen und x,y in AnimationPanel) die Daten sollten von Model gehalten und verwaltet werden, die View wertet diese nur aus und agiert dementsprechend.



1) Um besser durchzublicken, habe ich die Namensgebung bei den Objekten die "feuern" bis jetzt so gemacht, daß ich weiß welche der feuernden Objekte die Methode aufruft.

2) Um nicht "die Positionsdaten doppelt zu halten", habe ich - wegen euer Kritik - begonnen das Programm abzuändern.
Allerdings bekomme ich laufend eine NullpointerException.
Ich habe jetzt schon länger daran rumgemacht und weiß nicht woher diese kommt.
Außerdem wird mein Button erst dann angezeigt, wenn die Fenstergröße durch den Anwender verändert wird.
Deshalb bringe ich unten die geänderte Version.
Weiß jemand, wo sich mein Fehler befindet ?

mfg
Ernst



```
package wagenanimationmvc15;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;

class AnimationController {
    private AnimationView animationView;
    private AnimationModel animationModel;
    
    public AnimationController(int viewlaenge, int viewbreite, int strassenlaenge, int strassenbreite) {
        animationModel = new AnimationModel(strassenlaenge, strassenbreite);        
        animationView = new AnimationView(animationModel, viewlaenge, viewbreite);
        addListener();
    }
    
    public void addListener(){
        // Button + Wanze
        ButtonSchnellerActionListener buttonSchnellerActionListener;
        buttonSchnellerActionListener= new ButtonSchnellerActionListener(animationModel);
        animationView.setButtonSchnellerListener(buttonSchnellerActionListener);
        // Timer + Wanze
        Timer timer;        
        TimerActionListener tal;
        tal=new TimerActionListener(animationModel);
        timer = new Timer(2,tal);
        timer.start();
    }
    
    public AnimationView getAnimationView() {
        return animationView;
    }
}

class TimerActionListener implements ActionListener {
    private AnimationModel animationModel;    

    public TimerActionListener(AnimationModel animationModel){
        this.animationModel=animationModel;
    }
    
    public void actionPerformed(ActionEvent ae) {
        animationModel.updateFromTimerTimer();
    }
}


class ButtonSchnellerActionListener implements ActionListener {
    private AnimationModel animationModel;    

    public ButtonSchnellerActionListener(AnimationModel animationModel){
        this.animationModel=animationModel;
    }
    
    public void actionPerformed(ActionEvent ae) {
        animationModel.updateFromButtonSchneller();
    }
}
```


```
package wagenanimationmvc15;
import java.awt.Color;
import java.awt.Graphics;

class AnimationJPanel extends javax.swing.JPanel {
    // Daten für Methode paintComponent() ---
    private Wagen wagen;
    
    public void setWagen(Wagen wagen){
        this.wagen=wagen;
    }

    
    
    public AnimationJPanel() {
    }
    
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(Color.red);
        g.fillRect(wagen.getOrtX(), wagen.getOrtY(), 30, 25);        
    }
}
```


```
/*
AnimationModel wird überwacht, deswegen muß es von der Klasse Observable
erben. Durch die Anweisungen:
setChanged();
notifyObservers();        
in einer Methode f(), wirft eine überwachte Klasse ein spezielles 
Objekt, das von der Methode update einer überwachenden Klasse
eingefangen wird. 
*/  

package wagenanimationmvc15;

import java.util.Observable;

class AnimationModel extends Observable{
    private Wagen wagen;
    private int strassenlaenge;
    private int strassenbreite;    

    public Wagen getWagen(){
        return wagen;
    }
    
    public void updateFromTimerTimer() {
        wagen.fahren();
        setChanged();
        notifyObservers();        
    }

    public void updateFromButtonSchneller() {
        wagen.veraendereSchritt(1);
        setChanged();
        notifyObservers();        
    }
    
    
    public AnimationModel(int strassenlaenge, int strassenbreite) {
        wagen=new Wagen(strassenlaenge, strassenbreite);
    }
}
```


```
Label;
import javax.swing.JPanel;


/*
AnimationView ist eine überwachende Klasse. Deswegen muß sie die 
Schnittstelle Observer implementieren und unbedingt die Methode
update() ausprogrammieren. 
update() wird immer dann aufgerufen, wenn sich in einer überwachten
Klasse etwas ändert (dies muß durch die Anweisungen setchanged()
und notifyObservers() vorbereitet werden).        
*/  
class AnimationView extends javax.swing.JFrame implements Observer{
    private AnimationJPanel animationJPanel;
    private AnimationModel animationModel;
    private JButton buttonSchneller;    

 
    
    public AnimationView(AnimationModel animationModel, int viewlaenge, int viewbreite) {
        JPanel buttonsJPanel;
        BorderLayout borderLayout;
        GridLayout gridLayout_1_2;
        Container mycont;        
        this.animationModel = animationModel;
        // Überwachung: Es wird ein Überwacher (=Wanze, Detektiv) an
        // dem Objekt animationModell angebracht.
        this.animationModel.addObserver(this);
        // Buttons, Panels und Layouts erstellen
        buttonSchneller=new JButton("schneller");
        buttonsJPanel=new JPanel();
        animationJPanel = new AnimationJPanel();        
        gridLayout_1_2=new GridLayout(1,2);
        borderLayout=new BorderLayout ();
        mycont = getContentPane();                
        // Panels mit Layout formatieren
        mycont.setLayout(borderLayout);        
        buttonsJPanel.setLayout(gridLayout_1_2);                
        // An Panels Buttons montieren
        buttonsJPanel.add(buttonSchneller);
        // Dummy-Label montieren
        buttonsJPanel.add(new JLabel());                
        // An mycont die Panels montieren
        mycont.add(animationJPanel,BorderLayout.CENTER);
        mycont.add(buttonsJPanel,BorderLayout.SOUTH);        
        this.setSize(viewlaenge, viewbreite);        
        this.setVisible(true);        
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);        
    }
    
    public AnimationJPanel getAnimationJPanel(){
        return animationJPanel;
    }

    /*    
    public void setNewView(){
        Wagen w;
        w = animationModel.getWagen();
        //animationJPanel.setX(w.getOrtX());        
        //animationJPanel.setY(w.getOrtY());                
    }
*/
    
    // Wenn sich im überwachten Modell Daten aändern, wird ein Event
    // ausgelöst (ein spezielles Objekt geworfen) das von der Methde
    // update() eingefangen wird. Diese Methode wird dann also aufgerufen
    public void update(Observable m, Object o) {
        if (m == animationModel) {
        Wagen w;
        w = animationModel.getWagen();
            //setNewView();
            animationJPanel.setWagen(w);
            animationJPanel.repaint();
        }
    }
    
    public void setButtonSchnellerListener(ButtonSchnellerActionListener al) {
        buttonSchneller.addActionListener(al);
    }
}
```


```
/*
R O G R A M M B E S C H R E I B U N G
Zusätzlich wird noch ein Button einegfügt, mit dem man einen neunen 
Wagen erstellen kann.


*/

package wagenanimationmvc15;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Observable;
import java.util.Observer;
import javax.swing.*;

public class MainWagenAnimationMVC15 {

    public static void main(String[] args){
        AnimationController animationController = new AnimationController(500, 500, 400, 400);
    }
}
```


```
package wagenanimationmvc15;

class Wagen{
    private int ortX;
    private int ortY;
    private int schritt;
    private int strassenlaenge;
    private int strassenbreite;    

    public Wagen(int strassenlaenge, int strassenbreite){
        ortX=0;
        ortY=0;
        schritt = 1;        
        this.strassenlaenge=strassenlaenge;
        this.strassenbreite=strassenbreite;

    }

    public void setOrtX(int pOrtX){
        ortX = pOrtX;
    }

    public int getOrtX(){
        return ortX;
    }

    public void setOrtY(int pOrtY){
        ortY = pOrtY;
    }

    public int getOrtY(){
        return ortY;
    }
    
    public void veraendereSchritt(int wert){
        schritt=schritt+wert;
    }
    
    void fahren(){
        if(ortX <= strassenlaenge){
            ortX = ortX + schritt;
        }
        else{
            ortX=0;
        }
    }

    void setSchritt(int pSchritt){
        schritt = pSchritt;
    }

}
```


----------



## ernst (25. Aug 2012)

Habe den Fehler gefunden. Leider weiß ich nicht, warum diese NullPointerException erst zur Laufzeit erscheint. Habe dies in einem neuen Thread gepostet:

http://www.java-forum.org/awt-swing-swt/140660-problem-timer-swing.html#post933453


mfg
Ernst


----------

