# Wie: Eigener Listener, eigenes Event (möglichst einfach)



## ernst (6. Mai 2007)

Hallo allerseits,
ich will in (mit Hilfe einer Schleife) ein Bild (image) immer wieder etwas einzeichnen (z.B. einen Kreis).
Bei jedem Einzeichnen eines Kreises soll ein (selbstgebasteltes?) Ereignis erzeugt werden, durch das dann ein (selbstgebastelter) Listener aufgerufen wird.
Ich habe schon zwar ein paar Sachen darüber gelesen, aber ich habe es bis jetzt noch nicht geschafft ein funktionsfähiges Programm zu schreiben. Ich bekomme es einfach nicht hin.

Kann mir jemand ein _lauffähiges_ - möglichst einfaches Programm - geben (das die Kreise einzeichnet und z.B. bei jedem Einzeichnen den Listener aufruft, der eine Meldung auf dem Bildschirm ausgibt), so dass ich daran das Prinzip erkennen kann.

mfg
Ernst


----------



## dieta (6. Mai 2007)

Mal vorweg:



			
				Vor dem ersten Posten bitte lesen!!! hat gesagt.:
			
		

> - Wir machen keine Hausaufgaben. Ihr müsst schon eigene Ideen, bzw. Lösungsvorschläge haben.




An sonsten: Beide Teilfragen die du hast, werden in den FAQs reecht ausführlich behandelt. Die Foren-Suche und Google helfen auch weiter.

http://www.gidf.de/


----------



## Guest (7. Mai 2007)

>
>Wir machen keine Hausaufgaben. Ihr müsst schon eigene Ideen, bzw. Lösungsvorschläge haben.  
>
ich muss keine Hausaufgaben mehr machen.
>
>An sonsten: Beide Teilfragen die du hast, werden in den FAQs reecht ausführlich behandelt. 
>
Meinst du etwa:
http://www.java-forum.org/de/viewtopic.php?t=3569
Das hilft mir leider auch nicht!
>
>Die Foren-Suche und Google helfen auch weiter. 
>
Leider nein.
>
>http://www.gidf.de/ 
>
Das hat mir leider auch nicht geholfen.

Trotzdem Dank für deine Hilfe.
Da du dich offenbar auskennst (“Die Foren-Suche und Google helfen auch weiter“) bitte ich dich, mir einen _konkreten_ Link oder den Quellcode zu geben, in dem ein _lauffähiges_ Programm vorkommt, da ich leider schon ziemlich viel rumprobiert habe und nicht weitergekommen bin. 

mfg
Ernst


----------



## SlaterB (7. Mai 2007)

> Kann mir jemand ein _lauffähiges_ - möglichst einfaches Programm - geben [..], so dass ich daran das Prinzip erkennen kann

du verdrehst die Aufgaben:
Kannst DU ein _lauffähiges_ - möglichst einfaches Programm - geben, so dass man daran deine bisherigen Versuchen, Fehler und weiteren Probleme erkennen kann?!
Eine textuelle Beschreibung außer 'hab viel versucht, nix geht' wäre natürlich (alternativ/ ergänzend) auch nicht schlecht.

Ganz ohne Grundlagen gehts natürlich nicht,
schaue dir die Klassen Observer, Observable an,
http://www.galileocomputing.de/openbook/javainsel6/javainsel_08_006.htm#Xxx1000605


----------



## ernst (7. Mai 2007)

>
>du verdrehst die Aufgaben: 
>Kannst DU ein _lauffähiges_ - möglichst einfaches Programm - geben, 
>so dass man daran deine bisherigen Versuchen, Fehler und weiteren Probleme erkennen kann?! 
>Eine textuelle Beschreibung außer 'hab viel versucht, nix geht' wäre natürlich
>(alternativ/ ergänzend) auch nicht schlecht. 
>
>Ganz ohne Grundlagen gehts natürlich nicht, 
>schaue dir die Klassen Observer, Observable an, 
>http://www.galileocomputing.de/openbook/javainsel6/javainsel_08_006.htm#Xxx1000605
>
Für Observable/Observer habe ich ein funktionierenes Programm. Aber ich will mein Programm nicht mit Observable/Observer realisieren, sondern mit Listener/Event!

Nur: mit Listener/Event gibt es Probleme. 
Ich habe irgendwie ein paar Programmteile zusammengefügt, aber das hat alles nur Schrott gegeben.

Ich muss eine eigene Listener-Klasse z.B. MyListenerKlasse (implementiert EventListener) und eine eigene Event-Klasse z.B. MyEventKlasse (extends EventObject) bauen.
Wie kann ich aber nun dem System mitteilen, dass beim Zeichnen eines Kreises auf das Image ein Objekt _meiner_ selbstgebastelten Listener-Klasse MyListenerKlasse erzeugt wird?
Brauche ich dazu noch irgendwelche Methoden?

Bei den vorgegebenen (im Java-System schon existierenden) Listenern ist mir das klar:
Bei einem Mausklick wird durch das Java-System (VM?) ein Ereignis vom Datentyp einer im Java-System schon implementierten Klasse erzeugt.
Aber wie ist es, wenn ich alles selber basteln muss?


mfg
Ernst


----------



## kleiner_held (7. Mai 2007)

Das ist doch an sich wirklich kein kompliziertes Konzept.
Deine Klasse die das Zeichnen vornimmt und die Events ausloesen soll muss folgendes implementieren:
1. 2 Methoden addMyListener(MyListenerKlasse listener) und removeMyListener(MyListenerKlasse listener) die die Listener intern z.B. in einer Liste verwalten
2. eine private oder protected Methode  fireMyEvent(MyEventKlasse event) die den Event an alle deine registrierten Listener aus der obrigen Liste ausloest
3. den Aufruf von fireMyEvent() mit einem neuen MyEvent an allen Stellen im Code, an denen du einen Event ausloesen willst.


----------



## SlaterB (7. Mai 2007)

jo, und du kannst nur vom System vorgegebene Events wie MausEvents nutzen, um bei deren Bearbeitung eigene Events auszulösen,
was leicht unsinnig ausschaut,

du kannst die Maus nicht so konfigurieren, dass sie direkt was anderes macht, dazu ist Java nicht gedacht 

ob das nun Observable oder Listener heißt ist da doch egal?


----------



## ernst (7. Mai 2007)

kleiner_held hat gesagt.:
			
		

> Das ist doch an sich wirklich kein kompliziertes Konzept.
> Deine Klasse die das Zeichnen vornimmt und die Events ausloesen soll muss folgendes implementieren:
> 1. 2 Methoden addMyListener(MyListenerKlasse listener) und removeMyListener(MyListenerKlasse listener) die die Listener intern z.B. in einer Liste verwalten
> 2. eine private oder protected Methode  fireMyEvent(MyEventKlasse event) die den Event an alle deine registrierten Listener aus der obrigen Liste ausloest
> 3. den Aufruf von fireMyEvent() mit einem neuen MyEvent an allen Stellen im Code, an denen du einen Event ausloesen willst.



Das ist der Punkt:
Du schreibst _eigene_ Methoden 
fireMyEvent()
addMyListener(MyListenerKlasse listener) 
removeMyListener(MyListenerKlasse listener)
...
Was mir aber nicht klar ist. Wer garantiert, dass diese _selbstgebastelten_ Methoden das Gewünschte machen?
Wer bzw. welcher Mechanismus garantiert, dass das Java-System wirklich ein Ereignisobjekt erzeugt?
Da muss doch dann die Vererbung oder sonst was eine Rolle spielen (die mir dann _Funktionalität_ vererbt).

Ich kann doch auch nicht einfach eine Funktion schreiben, wie z.B.
char HoleByteVonSeriellerSchittstelle(){
...
}
Die holt mir garantiert nichts, wenn ich nicht auf schon _bestehende_ Ressourcen des Java-Systens zurückgreife, die irgendwas mit der seriellen Schnittstelle zu tun haben. Ansonsten müsste ich selbst über die Portadressen die serielle Schnittstelle ansprechen, was meiner Meinung nach nicht funktioniert (weil der der Prozessor im protected Mode läuft und er deshalb eine Hardware-Exception auslöst; unter DOS war so was mit C möglich).

mfg
Ernst


----------



## SlaterB (7. Mai 2007)

wie gesagt: wenn die Maus oder die serielle Schnittstelle etwas besonderes in deinem Programm machen soll, 
dann hat das erstmal nix mit Ereignisbehandlung oder Swing zu tun (Tenor in diesem Thread),
dann öffne einen neuen Thread  (a la 'Maus aus Systemebene kontrollieren')
oder such dir am besten eine passendere Programmiersprache 

mit Swing hast du einen MouseListener und eine Operation mousePressed
und da kannst du entweder nix tun oder eine eigene Operation
'createMyEvent' aufrufen,
keine große Kunst


----------



## kleiner_held (7. Mai 2007)

ernst hat gesagt.:
			
		

> Das ist der Punkt:
> Du schreibst _eigene_ Methoden
> fireMyEvent()
> addMyListener(MyListenerKlasse listener)
> ...



Irgendwie bin ich verwirrt - deine Frage handelt sich darum, wie man den Listner Mechanismus per Hand implementiert und dann willst du das das Java-System irgend etwas fuer dich macht? 

Das "Java System" wird fuer deine selbstgestrickten Events nie etwas fuer dich machen - wie auch, wenn du eigene Events generierst, dann weisst auch nur du wann denn das Ereignis eintritt und ein Event ausgeloest werden muss. Man kann doch nicht einfach eine Klasse EsIstVollmondEvent anlegen und darauf warten das deine JVM von selbst mitkriegt das Vollmond ist und ein Event ausgeloest werden soll. Du musst dich um alles selbst kuemmern, um das Registrieren der Listener, um das Anlegen Event Objekte und um das Senden dieser Events an alle Listener.

Vielleicht solltest du nochmal genau erklaeren was du eigentlich willst.


----------



## Tobias (7. Mai 2007)

Äh, die Implementierung eines eigenen Listeners habe ich doch (lauffähig, wenn man eine kleine main drumpackt) ausführlichst in der FAQ erklärt?? Den Link hat man dir ja schon gegeben ... Raff ich nicht, wie man damit nicht klar kommen kann.

mpG
Tobias


----------



## ernst (7. Mai 2007)

kleiner_held hat gesagt.:
			
		

> Irgendwie bin ich verwirrt - deine Frage handelt sich darum, wie man den Listner Mechanismus per Hand implementiert und dann willst du das das Java-System irgend etwas fuer dich macht?
> 
> Das "Java System" wird fuer deine selbstgestrickten Events nie etwas fuer dich machen - wie auch, wenn du eigene Events generierst, dann weisst auch nur du wann denn das Ereignis eintritt und ein Event ausgeloest werden muss. Man kann doch nicht einfach eine Klasse EsIstVollmondEvent anlegen und darauf warten das deine JVM von selbst mitkriegt das Vollmond ist und ein Event ausgeloest werden soll. Du musst dich um alles selbst kuemmern, um das Registrieren der Listener, um das Anlegen Event Objekte und um das Senden dieser Events an alle Listener.
> 
> Vielleicht solltest du nochmal genau erklaeren was du eigentlich willst.



Vermutlich ist die Verwirrung in meinem Kopf.
Wenn ich will, dass beim Zeichnen eines Kreises ein Ereignis ausgelöst werden soll, benötige ich doch Hilfe vom Java-System. Ich brauche doch irgendenen Befehl vom Java-System, der dann wirklich die VJM veranlasst ein Ereignis auszulösen. Ich kann zwar irgendeine Methode wie z.B. myBitteLoeseEreignisAus() schreiben, doch innerhalb dieser Methode benötige ich doch eine Methode des Java-Systems, die die dann die VJM veranlasst, dies zu tun
Oder bin ich da voll auf dem Holzweg?

mfg
Ernst


----------



## Tobias (7. Mai 2007)

Du hast eine Zeichenfläche (zum Beispiel eine Canvas). Du hast einen JToggleButton, auf dem steht "Kreis zeichnen". Der User markiert diesen Button und zieht anschließend auf der Zeichenfläche per Drag & Drop den Kreis aus. In der mouseReleased()-Methode des Canvas-MouseListeners stellst du jetzt fest "Aha, das Kreiszeichnen-Werkzeug ist aktiviert und die Maus wurde gerade wieder losgelassen. Also wurde gerade ein Kreis gezeichnet.". Und nachdem du das festgestellt hast, feuerst du einen KreisGezeichnetEvent an alle registrierten KreisZeichnerListener.

Wo ist das Problem?

mpG
Tobias

P.S.: Du kannst dich nicht in die Eventauslösenden Routinen von Java einklinken. Du kannst einen Listener anbieten, der auf den Event reagieren kann (zum Beispiel wie oben das Systemevent in ein eigenes "höher Aggregiertes" übersetzen), aber mehr nicht. Ist auch weder sinnvoll noch nötig.


----------



## kleiner_held (7. Mai 2007)

Du bist auf dem Holzweg, es gibt keinen speziellen JVM Mechanismus fuer sowas.

Du zeichnest deinen Kreis, legst selbet einen neues EventObjekt mit new an, Iterierst ueber alle registrierten Listener und rufst an jedem Listener ein Methode handleEvent(myEvent) auf.
Das Senden des Events passiert linear in dem gleichen Thread in dem du auch den Kreis gezeichnet hast, im Programmablauf behandeln erst alle Listener den Event und dann wird deine zeichneKreis() methode fortgesetzt.

Ich vermute Du denkst an sowas wie eine asyncrone Eventverarbeitung, also ein kurzer Methodenaufruf und dannach laeuft die Abarbeitung deiner Methode zeichneKreis() weiter, waehrend im Hintergrund parallel (sprich in anderen Threads) alle Listener mit dem Event gefuettert werden. Das ist aber unueblich. Um mal auf das Beispiel des MouseListeners zurueck zu kommen - wenn du einen MouseListener einer beliebigen Swing-Componente hinzufuegst, dann erfolgt die Abarbeitung eines mouseClicked(MouseEvent e) in deinem GUIThread (der sogenannte AWT-Event) und wenn Du in der Methode ein Thread.slepp(10000); einfuegen wuerdest, wuerdest Du feststellen, dass deine GUI nach dem Mausklick fuer 10 Sekunden haengt.


----------



## ernst (7. Mai 2007)

kleiner_held hat gesagt.:
			
		

> Ich vermute Du denkst an sowas wie eine asyncrone Eventverarbeitung, also ein kurzer Methodenaufruf und dannach laeuft die Abarbeitung deiner Methode zeichneKreis() weiter, waehrend im Hintergrund parallel (sprich in anderen Threads) alle Listener mit dem Event gefuettert werden. Das ist aber unueblich. Um mal auf das Beispiel des MouseListeners zurueck zu kommen - wenn du einen MouseListener einer beliebigen Swing-Componente hinzufuegst, dann erfolgt die Abarbeitung eines mouseClicked(MouseEvent e) in deinem GUIThread (der sogenannte AWT-Event) und wenn Du in der Methode ein Thread.slepp(10000); einfuegen wuerdest, wuerdest Du feststellen, dass deine GUI nach dem Mausklick fuer 10 Sekunden haengt.



Ich versuche mal mein Anliegen an dem folgenden Programm unten zu erklären.
In dem Programm werden verzögert Kreise auf dem Bildschirm ausgegeben.
Um das zu synchronisieren verwende ich pfuschmäßig die Variable "ichMaleGerade".

Jetzt will ich das Programm ohne die Synchronisation machen und das Programm so abändern, dass nach jeder Verzögerung (sleep(500)) ein Ereignis (Objekt einer selbstgebastelten Klasse) ausgelöst wird und dadurch automatisch ein selbstgebastelten Listener aufgerufen wird, der einen Kreis zeichnet. 
(Wobei ein Ereignis auslösen doch CPU-intern intern einen Hardware-Interrupt auslöst und dies im Java-System verwendet wird, also wiederum eine Ressorce, die _JVM bietet_ und vom Programmierer benutzt wird. Das ist wieder der Punkt, wo ich auf dem Schlauch stehe).  

Kann mir jemand dazu einen möglichst einfachen Quellcode (mit Listener und Event) geben, an dem ich das Problem dann verstehen kann. Ich glaube, dass ich ohne einen einfachen Quellcode das Problem nicht verstehen kann.



```
package de;
import java.awt.*;
import javax.swing.*;

public class MainVerzoegertZeichnen1 {
	  public static void main(String[] args){
	    JFrame f = new JFrame();
	    f.setSize(550,550);
	    Diagramm diagramm = new Diagramm(550, 550);
	    f.getContentPane().add(diagramm);
	    Thread t = new Thread(diagramm);
	    t.start();
	    f.setVisible(true);
	  }
	}

class Diagramm extends JPanel implements Runnable{
  private int xpAnz;
  private int ypAnz;
  private int i; 
  private Image myimg;
  private Graphics myg;
  private int sx; 
  private int sy;
  private boolean ichMaleGerade;

  public Diagramm(int xpAnz, int ypAnz){  
    i=0; 
    this.xpAnz=xpAnz;
    this.ypAnz=ypAnz;    
    myimg=null;  
    ichMaleGerade=true;    
  }
  
  public void paintComponent(Graphics g){
  	if(myimg==null){
  	    sx = this.getSize().width;  
  	    sy = this.getSize().height;
  	    myimg = createImage(sx, sy);
  	    myg = myimg.getGraphics();
  	}
  	myg.setColor(Color.red);
 	myg.drawOval(i , 2*i+15, 20, 20);
       ichMaleGerade=false;    
  	g.drawImage(myimg,0,0,null);
  }
	
  public void run(){
    while(i<200){
      this.repaint();
      while(ichMaleGerade==true){
      }
      ichMaleGerade=true;
      try{
        Thread.sleep(500);
      }
      catch(Exception e){}
      i = i+20;
    }
  }
}
```


mfg
Ernst


----------



## Tobias (7. Mai 2007)

> Wobei ein Ereignis auslösen doch CPU-intern intern einen Hardware-Interrupt auslöst und dies im Java-System verwendet wird, also wiederum eine Ressorce, die _JVM bietet_ und vom Programmierer benutzt wird.



Auf Java bezogen völliger Schwachsinn, darum, was die Hardware macht, kümmere ich mich als Programmierer überhaupt nicht.

Ein "Ereignis auslösen" heißt soviel wie "Callback-Methoden aufrufen und diesen Informationen über die gerade geschehene Aktion mitteilen".

mpG
Tobias


----------



## SlaterB (7. Mai 2007)

```
try{ 
        Thread.sleep(500); 
        paintCircle()
}
```
oder auch

```
try{ 
        Thread.sleep(500); 
        createAnyKindOfEvent()
}
```


----------



## kleiner_held (7. Mai 2007)

Abgesehen davon ist ein Listener/Event Mechanismus fuer dein Beispiel nichtmal unbedingt noetig.


```
import java.awt.*;
import java.awt.image.BufferedImage;

import javax.swing.*;

public class MainVerzoegertZeichnen1 {
     public static void main(String[] args){
       JFrame f = new JFrame();
       f.setSize(550,550);
       Diagramm diagramm = new Diagramm(550, 550);
       f.getContentPane().add(diagramm);
       Thread t = new Thread(diagramm);
       t.start();
       f.setVisible(true);
     }
   }

class Diagramm extends JPanel implements Runnable{
  private int xpAnz;
  private int ypAnz;
  private int i;
  private Image myimg;
  

  public Diagramm(int xpAnz, int ypAnz)
  { 
    i=0;
    this.xpAnz=xpAnz;
    this.ypAnz=ypAnz;   
    myimg = new BufferedImage(xpAnz, ypAnz, BufferedImage.TYPE_INT_RGB);
  }
  
  @Override
  public Dimension getPreferredSize()
  {
	  return new Dimension(xpAnz, ypAnz);
  }
  
  protected void paintComponent(Graphics g)
  {
     synchronized(myimg)
     {
    	 g.drawImage(myimg,0,0,null);
     }
  }
   
  public void addKreis()
  { 
     synchronized(myimg)
     {
        Graphics myg = myimg.getGraphics(); 
        myg.setColor(Color.red);
        myg.drawOval(i , 2*i+15, 20, 20);
        myg.dispose();
     }
  }

  public void run(){
    while(i<200){
      try{
        Thread.sleep(500);
      }
      catch(Exception e)
      {
      }
      i = i+20;
      addKreis();
      this.repaint();
    }
  }
}
```


----------



## ernst (7. Mai 2007)

>
>Auf Java bezogen völliger Schwachsinn, darum, was die Hardware macht, kümmere ich mich 
>als Programmierer überhaupt nicht.
>
Stimmt. Ich wollte nur ausdrücken, dass das Java-System etwas macht, das der Programmierer nutzen kann und das er nicht selbst machen muss.

mfg
Ernst


----------



## ernst (7. Mai 2007)

>
>Abgesehen davon ist ein Listener/Event Mechanismus fuer dein Beispiel nichtmal unbedingt noetig.
>
Danke für dein Programm. Mit einer Synchronisation (so wie Java es vorsieht) habe ich es noch nicht probiert (habe aber gerade davon gelesen) Ich werde mir das anschauen und hoffentlich davon lernen.

Trotzdem:
Wie macht man es mit dem Listener/Event Mechanismus

mfg
Ernst


----------



## Roar (7. Mai 2007)

ernst hat gesagt.:
			
		

> Stimmt. Ich wollte nur ausdrücken, dass das Java-System etwas macht, das der Programmierer nutzen kann und das er nicht selbst machen muss.


hm, irgendwie verstehst du das nicht. das "java-system" macht *nix*
dein listener/events zeug sind nur stinknormale *methodenaufrufe* wie du sie zig-fach in deinem code hast. nicht mehr. du rufst eine methode auf und der übergibst du ein interface und irgendwann später wird wieder eine methode aus dem interface aufgerufen. mehr ist da nich, mehr macht java da nicht. mehr steht auch nicht in den Observer/Observable klassen drin.  :gaen:


----------



## kleiner_held (7. Mai 2007)

Hier ein Beispiel mit Listenern.

Obwohl ich nur ein sich aenderendes Diagramm habe und das Diagramm weder die DiagrammPanel noch das JLabel kennt (und nichtmal weiss was ein DiagrammPanel ist), werden alle 7 Panels und das Label zeitnah aktualisiert - das ist der Zweck von Events/Listenern. In der main() methode siehst Du auch die Verwendung eines annonymen Listeners.


```
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.EventListener;
import java.util.EventObject;
import java.util.List;

import javax.swing.*;

public class MainVerzoegertZeichnen1
{
	public static void main(String[] args)
	{
		JFrame f = new JFrame();
		f.setSize(400, 200);
		final Diagramm diagramm = new Diagramm(100, 100);
		f.getContentPane().setLayout(new GridLayout(2, 4));
		for (int i = 0; i < 7; i++)
		{
			f.getContentPane().add(new DiagrammPanel(diagramm));
		}
		final JLabel label = new JLabel(Integer.toString(diagramm.getI()));
		label.setHorizontalAlignment(JLabel.CENTER);
		diagramm.addListener(new MyListener()
			{
				public void handleEvent(MyEvent event)
				{
					label.setText(Integer.toString(diagramm.getI()));
				}
			});
		f.getContentPane().add(label);
		Thread t = new Thread(diagramm);
		t.start();
		f.setVisible(true);
	}

	public static class DiagrammPanel extends JPanel implements MyListener
	{
		private Diagramm diagramm;

		public DiagrammPanel(Diagramm diagramm)
		{
			this.diagramm = diagramm;
			diagramm.addListener(this);
		}

		public Dimension getPreferredSize()
		{
			return new Dimension(diagramm.getWidth(), diagramm.getHeight());
		}

		protected void paintComponent(Graphics g)
		{
			diagramm.draw(g);
		}
		
		public void handleEvent(MyEvent event)
		{
			repaint();
		}
	}

	public static class Diagramm implements Runnable
	{
		private int width;

		private int height;

		private int i;

		private Image myimg;

		private List<MyListener> listeners = new ArrayList<MyListener>();

		public Diagramm(int xpAnz, int ypAnz)
		{
			i = 0;
			this.width = xpAnz;
			this.height = ypAnz;
			myimg = new BufferedImage(xpAnz, ypAnz, BufferedImage.TYPE_INT_RGB);
		}

		public void draw(Graphics g)
		{
			synchronized (myimg)
			{
				g.drawImage(myimg, 0, 0, null);
			}
		}

		public void addKreis()
		{
			synchronized (myimg)
			{
				Graphics myg = myimg.getGraphics();
				myg.setColor(Color.red);
				myg.drawOval(i, i, 20, 20);
				myg.dispose();
			}
			fireEvent();
		}

		public void run()
		{
			while (i < 60)
			{
				try
				{
					Thread.sleep(500);
				}
				catch (Exception e)
				{
				}
				i = i + 10;
				addKreis();
			}
		}

		public int getWidth()
		{
			return width;
		}

		public int getHeight()
		{
			return height;
		}

		public void addListener(MyListener listener)
		{
			listeners.add(listener);
		}

		public void removeListener(MyListener listener)
		{
			listeners.remove(listener);
		}

		protected void fireEvent()
		{
			MyEvent event = new MyEvent(this);
			for (MyListener listner : listeners.toArray(new MyListener[listeners.size()]))
			{
				listner.handleEvent(event);
			}
		}

		public int getI()
		{
			return i;
		}
	}

	public static interface MyListener extends EventListener
	{
		public void handleEvent(MyEvent event);
	}

	public static class MyEvent extends EventObject
	{
		public MyEvent(Object source)
		{
			super(source);
		}
	}
}
```


----------



## ernst (7. Mai 2007)

>Obwohl ich nur ein sich aenderendes Diagramm habe und das Diagramm weder die DiagrammPanel 
>noch das JLabel kennt (und nichtmal weiss was ein DiagrammPanel ist), werden alle 7 Panels und das 
>Label zeitnah aktualisiert - das ist der Zweck von Events/Listenern. In der main() methode siehst Du 
>auch die Verwendung eines annonymen Listeners.
>
>
Erst mal vielen, herzlichen Dank für deinen Quellcode.
Habe sofort mit meiner Eclipse Entwicklungsumgebung ein Projekt erzeugt.
Leider kommt bei der folgenden Zeile eine Fehlermeldung:

private List<MyListener> listeners = new ArrayList<MyListener>();

Eclipse kann mit <MyListener> nichts anfangen.

Ich kenne das übrigens auch nicht (so weit ich mich erinnern ist das unter C++ ein Template).
Geht das auch ohne die eckigen Klammern?

mfg
Ernst


----------



## kleiner_held (7. Mai 2007)

welche Java Version verwendest Du? Bei 1.4 oder älter gibts keine Generics, dann muss da einfach stehen:

```
private List listeners = new ArrayList();
```

und die for schleife in fireEvent() muss ungefähr so aussehen:

```
protected void fireEvent()
      {
         MyEvent event = new MyEvent(this);
         MyListener[] listenerArray = (MyListener[]) listeners.toArray(new MyListener[listeners.size()]);
         for (int i = 0; i < listenerArray.length, i++)
         {
              listenerArray[i].handleEvent(event);
         }
      }
```


----------



## ernst (7. Mai 2007)

>private List listeners = new ArrayList();[/code]
>
>und die for schleife in fireEvent() muss ungefähr so aussehen:
>protected void fireEvent()
>{
>MyEvent event = new MyEvent(this);
>MyListener[] listenerArray = (MyListener[]) listeners.toArray(new MyListener[listeners.size()]);
>for (int i = 0; i < listenerArray.length, i++)
>{
>listenerArray_.handleEvent(event);
>}
>
Bis auf das fehlende Semikolon hat alles gepaßt (das habe ich korrigiert). Es wird fehlerfrei kompiliert und das Programm läuft.
Jetzt schaue ich mir in Ruhe mal den Quellcode an und bedanke mich derweil bei dir ganz herzlich, da du mir sehr geholfen hast. Ich hoffe, dass dann wieder mehr Klarheit in mein verwirrtes Hirn kommt.

mfg
Ernst_


----------



## Guest (10. Mai 2007)

kleiner_held hat gesagt.:
			
		

> welche Java Version verwendest Du? Bei 1.4 oder älter gibts keine Generics, dann muss da einfach stehen:
> 
> ```
> private List listeners = new ArrayList();
> ...



Ich habe mir den Code angeschaut und habe dazu eine Frage:
MyListener[] listenerArray = (MyListener[]) listeners.toArray(new MyListener[listeners.size()]);

In meiner Doku steht über toArray folgendes:
Object[]  toArray(Object[] a)

1) Mir ist nicht klar warum man das Array a benötigt. Wird es mit return zurückgegeben?
Dann würde man es doch nicht als input benötigen?

2) Was mir auch noch nicht klar ist:
MyListener ist ein Interface. Von einem Interface dürfen aber keine Objekte erzeugt werden.
Das wird doch aber mit:
MyListener[] listenerArray = (MyListener[]) listeners.toArray(new MyListener[listeners.size()]);
gemacht, oder?

mfg
Ernst


----------



## kleiner_held (10. Mai 2007)

1. Der Parameter bei List.toArray() ist dazu da, der Liste zu sagen von welchem Typ der Array ist. Der Sachverhalt wird an dieser Stelle erklaert, deswegen spare ich mir mal die detailierten Ausfuehrungen.

2. Nein an der Stelle wird kein neuer MyListener erstellt, sondern es wird ein Array erstellt, der nur MyListener's enthalten kann. Als Inhalt bekommt er dann alle MyListener's aus der Liste zugewiesen. Wen Du in Hinsicht auf Arrays noch nicht ganz so bewandert bist, hier nochmal eine Literaturempfehlung: Java-Insel: 3.9 Arrays
ist uebrigens das gleiche Buch wie im obrigen Link.


----------



## ernst (11. Mai 2007)

kleiner_held hat gesagt.:
			
		

> 1.
> Der Parameter bei List.toArray() ist dazu da, der Liste zu sagen von welchem Typ der Array ist. Der Sachverhalt wird an dieser Stelle erklaert, deswegen spare ich mir mal die detailierten Ausfuehrungen.



Danke für die Links. Ich habe die Quelle gelesen und mir folgendes überlegt:

Wichtige Bemerkung zur Bezeichnung:
X sei eine Klasse, die das Interface Y implementiert.
Von X werde ein Objekt erzeugt:
X x;
dann sage ich:
a) x _ist_ ein X-Objekt (als solches im Arbeitsspeicher angelegt)
b) x _hat_ den Datentyp Y

Ist dies vernünftig, oder gibt es eine besser Möglichkeit der Bezeichnung?

1)
listeneres besteht aus Objekten, die aus einer anonymen Klasse, ich nenne
sie hier einfach mal Z, erzeugt wurden und die MyListener als Interface 
enthalten:
      diagramm.addListener(new MyListener()
         {
            public void handleEvent(MyEvent event)
            {
               label.setText(Integer.toString(diagramm.getI()));
            }
         });


Dies entspricht gleichwertig:

Z implements MyListener{
  public void handleEvent(MyEvent event){
    label.setText(Integer.toString(diagramm.getI()));
  }
}

Also bestehen die Elemente von listeneres aus Z-Objekten und nicht aus 
MyListener-Objekten.

Ist das richtig?


2)
Durch die Angabe von MyListener im Parameter von toArray wird erreicht, 
dass toArray die Elemente von listeners in listenerArray reinkopiert und der
Datentyp MyListener ist.

Ist das richtig?

Frage:
Welchen Sinn har dann aber noch der cast-Operator MyListener[] vor listeners?
Muss dies nicht immer der gleiche Datentyp wie das Objekt im Parameter sein?

3)
Mit dem Parameter 
new MyListener[listeners.size()] 
wird zwar ein Array mit dem Datentyp MyListener erzeugt, aber es werden keine 
MyListener-Objekte in dem Array erzeugt, sondern es wird ein Array erzeugt, das als 
Werte Referenzen mit dem Wert "null" hat.

Ist das richtig?         


4) Was mir noch völlig unklar ist:

diagramm.addListener(new MyListener()

ruft auf auf:

public void addListener(MyListener listener)
      {
         listeners.add(listener);
         System.out.println("hallo1");         
      }

Das geschieht doch nur _einmal_ (ich sehe in main() nur einmal den Aufruf diagramm.addListener(new MyListener() und nirgends eine Schleife).
Warum erscheint trotzdem mehrmals die Ausgabe 
hallo1

Wo ist mein Dekfehler?

mfg
Ernst


----------



## kleiner_held (11. Mai 2007)

ich beantworte mal in anderer Reihenfolge

4) Du hast schon richtig erkannt, in Zeile 24 wird genau ein annonymer Listener erstellt, der die Aufgabe hat das Label zu aktualisieren und dieser Listener  wird am Diagramm  registriert.
In Zeile 20 werden aber in einer Schleife 7 DiagrammPanel's angelegt, die das Diagramm als Prameter fuer ihren Constructor bekommen. Und im Constructor von DiagrammPanel registriert sich das DiagrammPanel selbst als Listener am Diagramm (Zeile 44). Das ist moeglich, da die Klasse DiagrammPanel das Interface MyListener implementiert.

1) ist richtig fuer den annonymen Listener von Zeile 24, allgemein koennen aber in dem Array beliebige Objkete sein, die 
MyListener implementieren. Um mal deinen Definitionen zu verwenden:
Die Elemente von listeneres bestehen aus Z-Objekten (1 mal), aber auch aus DiagrammPanel's (7 mal)

2) Der Cast ist noetig, da die Methode toArray() laut Deklaration nur einen Object[] array zurueckgibt. Der Compiler weiss nicht, das es in diesem Fall ein MyListener[] Array ist, deswegen muss man es ihm explizit sagen. Ab Java 1.5 ist dieser Cast aber tatsaechlich nicht mehr noetig, da der Compiler durch die Verwendung von Generics mitbekommt, dass der Typ des zurueckgegebenen Arrays gleich dem Typ des Parameter Arrys entspricht.

3) Richtig


----------



## ernst (11. Mai 2007)

kleiner_held hat gesagt.:
			
		

> ich beantworte mal in anderer Reihenfolge
> 
> 4) Du hast schon richtig erkannt, in Zeile 24 wird genau ein annonymer Listener erstellt, der die Aufgabe hat das Label zu aktualisieren und dieser Listener  wird am Diagramm  registriert.
> In Zeile 20 werden aber in einer Schleife 7 DiagrammPanel's angelegt, die das Diagramm als Prameter fuer ihren Constructor bekommen. Und im Constructor von DiagrammPanel registriert sich das DiagrammPanel selbst als Listener am Diagramm (Zeile 44). Das ist moeglich, da die Klasse DiagrammPanel das Interface MyListener implementiert.


------------------------------
Zu 4)
Ich habe folgendes nicht beachtet:
// wirkt sich auf diagramm in DiagrammPanel aus !!      	
this.diagramm = diagramm;
// wirkt sich auf diagramm in main aus !!
diagramm.addListener(this);
Aber jetzt ist mir es klarer.


Zu den anderen Punkten:
Ich schreibe es nochmals mit meinen Worten.
Wenn du damit einverstanden bist, nehme ich es zu meinen "Akten".

1)
listeneres besteht aus Objekten, die aus einer anonymen Klasse, ich nenne
sie hier einfach mal Z, erzeugt wurden und und DiagrammPanel-Objekten. 
Diese enthalten MyListener jeweils als Interface.
Der folgende Programmteil nimmt z-Objekte in listeneres auf: 

      diagramm.addListener(new MyListener()
         {
            public void handleEvent(MyEvent event)
            {
               label.setText(Integer.toString(diagramm.getI()));
            }
         });


Dies entspricht gleichwertig:
Z implements MyListener{
  public void handleEvent(MyEvent event){
    label.setText(Integer.toString(diagramm.getI()));
  }
}

Also bestehen die Elemente von listeneres aus Z-Objekten und DiagrammPanel-Objekten,
aber nicht aus MyListener-Objekten, aber diese Objekte haben alle den Datentyp
MyListener 

2)
Durch die Angabe von MyListener im Parameter von toArray wird erreicht, 
dass toArray die Elemente von listeners in listenerArray reinkopiert und der
Datentyp MyListener ist.

Frage:
Welchen Sinn har dann aber noch der cast-Operator MyListener[] vor listeners?
Muss dies nicht immer der gleiche Datentyp wie das Objekt im Parameter sein?

Antwort:
Wenn man es weg lassen würde, würde laut Deklaration von toArray der Datentyp
Object[] zurückgegeben.
Dann würde aber folgende Anweisung einen Compilerfehler ergeben:
MyListener[] listenerArray = listeners.toArray(new MyListener[listeners.size()]);
weil listenerArray ein Objekt vom Typ Object[] zugewiesen werden würde.


PS:
Das Programm ist ziemlich komplex, was man aber erst bei genauerer Durchsicht mitbekommt.

mfg
Ernst


----------

