Swing Eigene Komponente (JPanel) soll ChangeEvents verteilen

Hallo und frohes neues Jahr!

Ich habe eine bereits funktionierende Komponente auf Basis JPanel gebaut. Diese agiert für sich selber und hat einen gewissen Inhalt. Der Inhalt kann - auf Basis von MouseEvents - vom Nutzer verändert werden. Das Program kann diesen Inhalt zu jeder beliebigen Zeit auslesen oder ändern.

Jetzt meine Frage: Wenn der Nutzer den Inhalt geändert hat, macht es aus meiner Sicht Sinn, das Program davon über ein Event (ChangeEvent?) in Kenntnis zu setzen. Gibt es dafür irgendwelche Unterstützung? Also etwas, das ich mit einbinden kann? Ich kann die EventListenerList verwenden oder die Klassen Observer und Observable, gibt es noch etwas was ich übersehen habe, was näher an dem GUI orientierten ChangeListener (oder vielmehr an dessen Gegenstück) angelehnt ist?

Vielen Dank,
Stephan
 
Hallo Stephan,

das sieht imho nach einem Fall für MVC aus. Besagter Listener wäre dann ein Modellistener, der ja beim MVC-Konzept auch von der View implementiert wird.

Gruß,
André
 
Hallo, also MVC schien mir dafür etwas oversized... Die Komponente hat ja auch gekapselt intern das model im Prinzip, das macht m.E. nach Datenkapselung auch Sinn. Sie soll halt bloß Bescheid sagen, wenn sich eben dieser Wert geändert hat. MVC find ich toll, wenn ich umfangreiche Daten von der View trennen will / kann. Aber bei grafischen Komponenten sind die Dinger doch sehr eng miteinander verbunden, denkst du nicht?

Stephan
 
Nun du kannst natürlich einen PropertyChangedListener verwenden. Um den für deine Komponente zu machen, kannst du aber auch gleich einen eigenen Listener schreiben. Ist fast die gleiche Arbeit.
 
@bERt0r: Ja, den habe ich heute auch entdeckt, danke für den Hinweis. Gibt es denn etwas, dass ich dort einbinden kann? Wo ich z.B. bereits die addPropertyChangeListener() Methode mitbekomme? Und die interne Struktur für die Verwaltung der Listener Liste? Jetzt hab ich das erstmal über die ListenerList gemacht, geht, aber vielleicht geht's ja noch besser?!?

@Andre: Es war auch gar nicht der Umfang der Daten, der mir den Eindruck aufdrängt MVC wäre oversized, sondern eher, dass ich gar nicht wirklich Model, View und Controller trennen wollte. Ich finde, das macht durchaus Sinn diese Dinge innerhalb einer Komponente zusammen zu lassen. MVC ist toll, wenn die Daten von der View trennbar sind oder die Logik (Controller) trennbar ist, aber die Komponente macht erst Sinn, wenn Sie als View mit einer vernünftigen Logik sowie sinnvollen Daten verbunden ist? Außerdem stellt sich dann auch hier die Frage, ob es im Framework Klassen zu Wiederverwendung gibt?

Stephan
 
Nun, wenn du mal beschreiben würdes was du eigentlich machst, könnten wir dir auch konkrete Ratschläge geben, was "besser" geeignet ist 🙂
 
@Andre: ... aber die Komponente macht erst Sinn, wenn Sie als View mit einer vernünftigen Logik sowie sinnvollen Daten verbunden ist? Außerdem stellt sich dann auch hier die Frage, ob es im Framework Klassen zu Wiederverwendung gibt?

Das Swing Framework bietet etwa ein Dutzend Model-Varianten an, auf die wir zurückgreifen können: ButtonModel, ComboBoxModel, BoundedRangeModel, SingleSelectionModel, ListModel, ListSelectionModel, TableModel, TableColumnModel, TreeModel, TreeSelectionModel, Document. View+Controller bleiben in Deinem Fall wohl besser als Einheit zusammen. Wir können sie im Konstruktor mit einem "default" Model versehen. Sie kann dann ohne Weiteres, und trotzdem flexibel eingesetzt werden.

Gruß,
André
 
Also, ganz allgemein wollte ich bloß möglichst "sauber" eine eigene Komponente erstellen. D.h. mir ging es um den Ansatz, wie man das mit den Callbacks für grafische Komponenten grundsätzlich am einfachsten / sinnigsten machen kann. Im Speziellen habe ich mir eine kleine Komponente gebaut zur Eingabe einer Uhrzeit; Datum verwende ich etwas fertiges, aber Uhrzeit hatte ich nix gefunden. Und eine Komponente (im Allgemeinen, bei der "Uhr" natürlich auch) meldet ja einen veränderten "Inhalt" zurück an die Applikation...

BoundedRangeModel ist mir schon zu speziell, für eine Komponente die genau das tut, wäre es wohl ok. Deshalb bin ich auch gegen MVC - entweder müsste der Controller ein active Polling machen, oder ich brauche einen Listener zwischen Model und Controller und genau danach suche ich ja. So gesehen könnte ich die Komponente als View betrachten und die Applikation implementiert Model und Controller. Aber active Polling will ich ja nicht gar nicht.

@Andre: Hey, das ist vielleicht das Stichwort... An die xxxModels (nix erotisches hier! 😉 ) hatte ich gar nicht gedacht, ich war immer in der Ecke Event, Listener, etc. unterwegs und hab dort was gesucht. Vielleicht passt das ButtonModel für meine Komponente am Besten, da war ich bisher einfach noch nicht drauf gekommen...

Stephan
 
Ich hab dir ein kleines Beispiel gemacht, ist eigentlich weniger Arbeit als ich dachte mit dem PropertyChangeListener:
Java:
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.event.ChangeListener;

public class UhrPanel extends JPanel {

	List<ChangeListener> changeListeners=new ArrayList<ChangeListener>();
	private JTextField textField;
	public UhrPanel() 
	{
		
		textField = new JTextField();
		textField.setColumns(5);
		textField.setEditable(false);
		add(textField);
		TimerTask t=new TimerTask()
		{
			Calendar c=Calendar.getInstance();
			SimpleDateFormat format=new SimpleDateFormat("hh:mm:ss");
			@Override
			public void run() 
			{
				Date oldDate=c.getTime();
				c.add(Calendar.SECOND, 1);
				Date newDate=c.getTime();
				textField.setText(format.format(newDate));
				UhrPanel.this.firePropertyChange("uhrzeit", oldDate, newDate);
			}
		};
		Timer timer=new Timer();
		timer.scheduleAtFixedRate(t, 0, 1000);
	}
	
	public static void main(String args[])
	{
		EventQueue.invokeLater(new Runnable(){

			@Override
			public void run() {
				
				JFrame frame=new JFrame();
				UhrPanel uhr=new UhrPanel();
				frame.getContentPane().add(uhr,BorderLayout.CENTER);
				uhr.addPropertyChangeListener("uhrzeit",new PropertyChangeListener()
				{
					@Override
					public void propertyChange(PropertyChangeEvent e) {
						System.out.println("Uhrzeit changed to from "+e.getOldValue()+" to "+e.getNewValue());
					}
				});
				frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
				frame.pack();
				frame.setVisible(true);
			}	
		});
	}
}
 
Funktioniert tatsächlich. Wat so 'ne Pogge für'n Speck hat... 😉 Dann stellt sich noch eine theoretische Frage. Ich hab das jetzt (erstmal, damit ich weitermachen konnte) mit einem selbst erstellten ChangeListener/Event gebaut. Aber es gäbe ja verschiedene Möglichkeiten:
- ChangeListener (weil es das Schlüsselmerkmal dieser Komponente ist)
- ActionListener (weil es eben die Standardänderung/aktion ist, die mit dieser Komponente ausgelöst wird)
- PropertyListener (weil es eine Veränderung an einem Merkmal ist)

Welche davon passt am Besten zu den Event/Listener Konzepten, die in den Standardkomponenten bereits genutzt werden?

Stephan
 

Neue Themen


Zurück
Oben