JList mit Model und Observer -> Chaos, blicke nicht durch

Status
Nicht offen für weitere Antworten.

hdi

Top Contributor
Hallo,

ich blick grad irgendwie gar nicht mehr durch :roll:

Ich habe die Klassen

Code:
LogEntry
Log extends Observable implements ListModel
LogConsole extends JFrame implements Observer

Also Log hält eine ArrayList<LogEntry> (LogEntry ist nicht viel mehr als ein String),
und hat als Observer eine LogConsole.
Darüberhinaus bietet die Klasse eine Methode add(String) an, mit der man die ArrayList um
einen Log-Eintrag erweitern kann. In dieser Methode wird setChanged() und notifyObservers(), also die
LogConsole, aufgerufen.

Die LogConsole ist ein Fenster, dass eine JList hat. Sie wird erstellt durch:
list = new JList(Log), d.h. sie bekommt eine Instanz der Klasse Log, die ja ListModel implementiert.

Soweit gibt es keine Fehler, aber irgendwie weiss ich jetzt nicht so ganz wie ich den Zusammenhang machen soll?
Komme mit der Klasse JList eben nicht so gut zu Recht.

Die Frage lautet:

Was muss ich jetzt in die update() Methode der LogConsole hineinschreiben, dass er mir jedesmal, wenn
ich dem Log etwas adde, diese Nachricht in die JList einfügt und das anzeigt?

D.h. ich habe ja in der update-Methode eine Instanz von Log, denn das ist ja nun mal das, was die LogConsole
beobachtet.
Ich müsste jetzt einen String-Eintrag aus der ArrayList vom Log herausziehen und der JList adden, damit
die Nachricht entsprechend in meinem Konsolen-Fenster angezeigt wird.

Hab da den Überblick verloren, weil ich jetzt das Konzept von Observern verbinden muss mit irgendwelchen
ListModel-Interfaces und JLists, wovon ich keine Ahnung habe. Und das überfordert mein Anfänger-Hirn etwas :autsch:

Bin dankbar für Hilfe, falls man mein Problem nicht so genau versteht, einfach nochmal nachfragen ;)
 

0x7F800000

Top Contributor
wowowow, vollbremsung mal... dir ist schon bewusst, dass diese ganze model-view sache in swing schon drin ist? Siehe dir hier mal die erste zeile der tabelle an.
Wenn du das schon so über swing alles regelst, dann brauchst du eigentlich keine Observer-Gebilde drumrumzubauen, das wäre alles doppelgemoppelt.
 

hdi

Top Contributor
Achso, ok... naja hab grad nich so ganz den Überblick über diese JList Sache, und ListModel etc etc.

Muss mal etwas rumprobieren...
 

hdi

Top Contributor
Ok, danke hab jetzt hinbekommen dass sich das selbst organisiert etc.

Hab allerdings ein Problem mit dem ScrollPane und der JList, das ich schonmal hatte, die Lösung
klappt diesmal aber nicht.

Kann mir jemand sagen, warum in diesem Code:

Code:
public class LogConsole extends JFrame {

    private JList list;
    private JScrollPane pane;

    public LogConsole(Log log) {

        setPreferredSize(new Dimension(500, 500));
        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
        list = new JList(log);
        pane = new JScrollPane();
        pane.setViewportView(list);
        getContentPane().add(pane);
        pack();
    }
}

die Jlist hinter dem ScrollPane versteckt ist? Die Inhalte der JList werden nicht angezeigt!
Wenn ich hingegen das ScrollPane weglasse, und nur mach:

Code:
public class LogConsole extends JFrame {

    private JList list;
    private JScrollPane pane;

    public LogConsole(Log log) {

        setPreferredSize(new Dimension(500, 500));
        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
        list = new JList(log);
        getContentPane().add(list);
        pack();
    }
}

dann passt alles und der Inhalt der JList wird angezeigt. D.h. es kann ja eig. nicht an der JList liegen, oder?
Warum geht das mit setViewportView() nicht korrekt, warum erfasst das ScrollPane meine Liste scheinbar nicht?

edit: Ach und... neue Einträge während der Laufzeit werden NICHT angezeigt... Ich dachte das alles hat ein
eigenes Observern-Ding? Geht aber nicht. Man ich steig gar nicht mehr durch :( Diese Klassenflut immer und alles
hat tausend Listener und alles ist anders und dann doch nicht... Ich mach doch:

Code:
list = new JList(log);    // <---  "log" ist ja vom Typ einer Klasse implements ListModel

also warum beobachtet er das jetzt nicht und fügt neue Einträge im Log auch während der Laufzeit automatisch
in die Liste ein ??? Ich dachte JList und ListModel implementieren dieses Observer Pattern :cry:
 

Marco13

Top Contributor
Hm - das mit der Anzeige sollte eigentlich funktionieren. Poste ggf. mal ein kleines, Compilierbares Beispiel, wo man das Problem nachvollziehen kann.

Zur Observer-Sache: Du hast ja
Log extends Observable implements ListModel
bzw. jetzt wohl nurnoch
Log implements ListModel
Wie sind denn dort die Methoden
addListDataListener(ListDataListener l)
usw. implementiert? Vermutlich wäre es am einfachsten, wenn du
Log extends AbstractListModel implements ListModel
oder sogar
Log extends DefaultListModel implements ListModel
verwenden würdest.

Wenn man eine JList erstellt
JList list = new JList(listModel);
dann wird diese JList dem listModel als ListDataListener hinzugefügt. Wenn sich der inhalt des ListModels ändert, dann müssen alle ListDataListeners über diese Änderung benachrichtigt werden. AbstractListModel bietet dafür schon hilfreiche Methoden, und DefaultListModel implementiert diesen Mechanismus schon komplett.
 

Landei

Top Contributor
Hat jetzt wahrscheinlich nichts mit deinem Problem zu tun, aber du brauchst normalerweise keine extra Membervariable für JScrollPane. Ich schreibe immer:

Code:
getContentPane().add(new JScrollPane(list));
 

0x7F800000

Top Contributor
irgendwie kommt es mir mittlerweile echt so vor, als ob du von den ganzen deinen klassen "Log" "LogEntry" und "LogConsole" nichts mehr bräuchtest, und stattdessen alles mit stinknormalen fertigen List<String> und JList und JFrame zusammenbauen solltest.

Ich meine, du hast jetzt irgendwelche wrapper-klassen drumrumgebaut, die eigentlich im wesentlichen nichts interessantes machen. Aber dafür hast du das problem, dass du dauernd irgendwelche methoden, die ja eigentlich schon da sind, durch deine wrapper klassen verschleierst und versteckst, und dadurch natürlich die ganze Observations-Kommunikation nicht mehr richtig funzt.
 

hdi

Top Contributor
Hm, also ich war mir nicht bewusst dass ich hier unnötige Wrapper-Klassen baue. Eigentlich dachte ich bisher,
dass es einigermassen professionell ist, wie ich es mache.

Ich meine statt der Klasse LogEntry kann ich natürlich auch gar keine extra Klasse machen, und einfach
direkt in der Konsole mit ArrayList<String> arbeiten.
Aber LogEntry ist nun mal nicht einfach ein String, sondern ein spezieller String der noch merh bietet. OO heisst doch auch, dass man abstrahiert und Klassen baut, damit die ÜBersicht gewährleistet wird.

Und Log und Console - so dachte/denke ich - ist auch gut zu trennen, denn das eine sind interne Daten, das andre ist die Darstellung dieser Daten. Deshalb hab ich 2 Klassen dafür, und nicht eine die das zusammenschmeisst.

So macht man das doch richtig oder? Mir braucht keiner erzählen, dass es leichter ist, alles zusammenzuwerfen.
Das weiss ich selbst auch, diese ganzen Konzepte wie MVC braucht man eigentlich nicht, man hat eine Klasse,
eine main-Methode, jede Variable ist überall sichtbar -> perfekt.
Leider ist da snicht gut, weil unsicher, d.h. unprofessionell = schlecht! Also mach ich mir bewusst die Mühe, und
versuche Klassen zu trennen, Polymorphismus zu nutzen, Dinge zu verstecken, nur über MEthoden zu arbeiten, etc.
Ist das nun falsch :bahnhof:

Mein Problem ist halt "einfach", dass ich mit diesen fertigen Klassen nicht so klarkomm. Man kann die API lesen,
dann weiss man was es in etwa ist, wie man eine Instanz erstellt, und was für Methoden es gibt. Aber wie man dann
zB so ein JList <-> ListModel Konzept wirklich realisiert, steht da nicht wirklich geschreiben.
Es gibt zwar Beispiele, aber die sind schon sehr sehr primitiv, und erklären nicht genau, was man mit den ganzen
Methoden etc anstellen kann oder muss.

Ich fände es sehr nett, wenn ihr mir nochmal das mit ListModel erklärt. Ich hatte nicht alle Methoden überschrieben,
ich wusste auch gar nicht was die alle bedeuten. Nur addElement() hatte ich implementiert (das war geraten), und dann wurden die Anfangs-Einträge auch angezeigt, aber während der Laufzeit keine neuen...

Was sind denn diese Mthoden add/remove DataListener beim ListModel Interface? Was muss ich da reinschreiben?
Ich brauch einfach ne Übersicht, wie das intern funktioniert damit ich damit arbeiten kann. Ich muss ja wissen,
welche Methode jetzt den Observer (der da ja irgendwie integriert ist anscheinend) anstösst, und wann das passiert,
etc etc. Ich hab ja nix dagegen, es nicht selbst zu machen mit Observer, aber ich muss schon verstehen, wie es die Klasen von Haus aus machen, sonst krieg ich's nicht hin :(

Welche Methoden von ListModel muss ich WIE implementieren? Für was stehen die, vorallem eben das mit dem DataListener...

Und das zweite ist halt noch immer das Problem, dass ich eineKlasse suche, die die LogNachrichten im Fenster
schön anzeigen kann. TextArea kann nix! HAt das JList schon alles? Kann ich da Farben einstellen etc?

Puh.... also Java kann hart sein :p Es gibt einfach so viele fertige Dinge, die man nehmen soll, aber is schon schwer zu verstehen wenn man sie nicht selber gemacht hat. Die API kratzt leider nur an der Oberfläche, oder mir fehlen ein paar wichtige Links, wo man das alles wirklich lernt. (Java Insel etc ist auch nur "Trash", damit meine ich jede Klasse wird in 20 Zeilen vorgestellt und fertig... Die API macht es echt nicht anders...)

Als Hilfe bzw. damit ihr noch mal nen Überblick habt, wie das gane aussieht, hier meine Klassen im Moment:
(das ist nun wieder mit Observer gemacht, weil ich es so hinkrieg...Wie baut man das jetz um, sodass es besser ist?)

Code:
public class Log extends Observable {

    private static final Log LOG = new Log();
    private ArrayList<LogEntry> entries;

    public Log() {

        entries = new ArrayList<LogEntry>();
        this.addObserver(new LogConsole());
    }

    public LogEntry getLatest() {

        return LOG.entries.get(LOG.entries.size() - 1);
    }

    public static void add(String message) {

        LOG.entries.add(new LogEntry(message));
        LOG.setChanged();
        LOG.notifyObservers();
    }
}

Code:
public class LogEntry {

    private String message;
    private String addInfo;

    public LogEntry(String message) {

        StackTraceElement ste = Thread.currentThread().getStackTrace()[3];
        String className = ste.getClassName().split("\\.")[1];
        String methodName = ste.getMethodName();
        String line = "" + ste.getLineNumber();
        String time = new SimpleDateFormat("HH:mm:ss").format(new Date());
        this.addInfo = time + " " + className + ":" + methodName + "(" + line + ")";
        this.message = message;
    }

    public String getMessage() {

        return message;
    }

    public String getAddInfo() {

        return addInfo;
    }
}

Code:
public class LogConsole extends JFrame implements Observer {

    private final int X_DIM = 250;
    private final int Y_DIM = 200;
    private JEditorPane editor;
    private JScrollPane scroll;

    public LogConsole() {

        setTitle("LogConsole");
        setResizable(false);
        setAlwaysOnTop(true);
        setPreferredSize(new Dimension(X_DIM, Y_DIM));
        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
        setUpScroll(setUpEditor());
        pack();
        setLocationByTray();
        setVisible(true);
    }

    private Component setUpEditor() {

        editor = new JEditorPane();
        editor.setEditable(false);
        editor.setFont(new Font("Courier",Font.PLAIN,11));
        editor.setBackground(Color.black);
        editor.setForeground(Color.green);
        return editor;
    }

    private void setUpScroll(Component editor) {

        scroll = new JScrollPane();
        scroll.setViewportView(editor);
        this.add(scroll);
    }

    private void setLocationByTray() {

        int width, height;
        Rectangle visibleDesktop =
                GraphicsEnvironment.getLocalGraphicsEnvironment().
                getMaximumWindowBounds();
        width = (int) visibleDesktop.getWidth();
        height = (int) visibleDesktop.getHeight();
        setLocation(width - X_DIM, height - Y_DIM);
    }

    public void update(Observable o, Object arg) {

        Log log = (Log) o;
        String previous = editor.getText();
        String next =   log.getLatest().getAddInfo() 
                        + "\n" +
                        log.getLatest().getMessage();
        editor.setText(previous + insertNewLine() + next);
        editor.setCaretPosition(editor.getText().length());
    }

    private String insertNewLine() {

        if (editor.getText().equals("")) 
        return "";
        return "\n";
    }
}
 

Marco13

Top Contributor
Eigentlich dachte ich bisher, dass es einigermassen professionell ist, wie ich es mache.

ICH würde das von MIR nicht behaupten. Die Gefahr, dass ich jemanden treffe, der mich eines Besseren belehrt, wäre mir zu hoch.

Wie auch immer ... ein Problem ist vielleicht...

ich wusste auch gar nicht was die alle bedeuten. Nur addElement() hatte ich implementiert (das war geraten),...

Also - du hast ja selbst das MVC erwähnt. Man hat irgendein Datenmodell. Wenn sich das Datenmodell verändert, werden alle Listener benachrichtigt. Wer sich da als Listener registriert, ist egal. Aber WENN sich jemand als Listener registriert, muss man ihm bei einer Änderung auch bescheid sagen (und das hast du ja jetzt offenbar nicht gemacht).

Die Grundstruktur einer Model-Klasse ist also wie folgt:
Code:
class SomeModel
{
    private List<SomeListener> listeners = ...

    public void addSomeListener(SomeListener someListener) { listeners.add(someListener); }
    public void removeSomeListener(SomeListener someListener) { listeners.remove(someListener); }

    public void changeSomethingInTheModel()
    {
        // Change some data...
        data = newData;
        ...
        // Notify all listeners
        fireSomethingChanged();
    }

    protected void fireSomethingChanged()
    {
        SomeEvent event = new SomeEvent(...);
        for (SomeListener someListener : listeners) { somelistener.somethingChanged(event); }
    }
}

Wenn man seine eigene Implementierung eines ListModels hat, dann muss man die Liste aller ListDataListeners, die mit addListDataListener hinzugefügt werden, speichern. Wenn in diesem ListModel dann "addElement" aufgerufen wird, dann muss man ...
1. Einen ListDataEvent erstellen, der die Änderung beschreibt
2. Durch die Liste der ListDataListener gehen, und auf jedem "intervalAdded(ListDataEvent e)" aufrufen.
(genau das wird beim AbstractListDataModel gemacht, wenn man fireIntervalAdded aufruft)

Bei einem ListModel gibt's halt viele mögliche Events - Daten können hinzugefügt, entfernt, geändert werden usw. Deswegen ist dieser Mechanismus schon vor-implementiert: Im AbstactListModel liegen schon alle Methoden, die sich darum kümmern. Wenn man ein eigenes ListModel erstellen will, kann man von AbstractListModel erben, um sich diese Arbeit zu sparen. Aber man muss diese Methoden immernoch AUFRUFEN wenn die jeweiligen Ereignisse eintreten.

Am einfachsten ist es, von DefaultListDataModel zu erben. Da ist ALLES schon mit drin.

Konkret zur JList: Wenn man eine JList mit einem bestimmten ListModel erstellt, dann wird dem ListModel ein Listener hinzugefügt, der dafür sorgt, dass die JList bei jeder Änderung aktualisiert wird.

Zu deinen Klassen:

An sich ist der Grundgedanke hinter so einer Log-Klasse ja nicht verkehrt. Auch nicht grundsätzlich dahinter, sie Observable zu machen - auch wenn man sich da auch andere Ansätze vorstellen könnte. Was du jetzt gepostet hast, ist EINE Möglichkeit. Eine Alternative wäre, dass man intern verschiedene Log-Implementierungen verwendet, die dann ggf. auch ausgetauscht werden können.

Hab' dein Beispiel mal ein bißchen aufgebohrt...
Code:
import javax.swing.*;
import java.util.*;
import java.awt.*;
import javax.swing.event.*;
import java.text.*;

class LogDemo
{
    public static void main(String args[])
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                LogConsole logConsole = new LogConsole();
                Log.doAddObserver(logConsole);
                test("Hallo NUR an die normale LogConsole!");


                JListLogConsole listLogConsole = new JListLogConsole();
                Log.doAddObserver(listLogConsole);
                test("Hallo an beide log consolen...");
                test("Das");
                test("kommt");
                test("in");
                test("die");
                test("Liste");

                Log.doDeleteObserver(logConsole);
                test("Das geht NURnoch in die Listen-Console");
            }
        });
    }


    private static void test(String message)
    {
        Log.add(message);
    }
}



class JListLogConsole implements Observer
{
    private DefaultListModel listModel;

    public JListLogConsole()
    {
        JFrame f = new JFrame();

        listModel = new DemoListModel();
        JList list = new JList(listModel);
        f.getContentPane().add(new JScrollPane(list));
        f.setSize(400,400);
        f.setVisible(true);
    }


    public void update(Observable o, Object arg)
    {
        Log log = (Log) o;
        String next =   log.getLatest().getAddInfo() + "\n" + log.getLatest().getMessage();
        listModel.addElement(next);
    }

}



// XXX Nicht nötig, nur zur Verdeutlichung!
class DemoListModel extends DefaultListModel
{
    public void addElement(Object object)
    {
        System.out.println("Im DefaultListModel wird 'addElement' aufgerufen");
        super.addElement(object);
    }

    protected void fireIntervalAdded(Object source, int index0, int index1)
    {
        System.out.println("Im DefaultListModel wird 'fireIntervalAdded' aufgerufen (benachrichtigt alle Listener)");
        super.fireIntervalAdded(source, index0, index1);
    }
}




class Log extends Observable {

    private static final Log LOG = new Log();
    private ArrayList<LogEntry> entries;

    public Log() {

        entries = new ArrayList<LogEntry>();
        //this.addObserver(new LogConsole()); // XXX Sollte von AUSSEN gemacht werden!
    }

    public static void doAddObserver(Observer observer)
    {
        LOG.addObserver(observer);
    }

    public static void doDeleteObserver(Observer observer)
    {
        LOG.deleteObserver(observer);
    }

    public LogEntry getLatest() {

        return LOG.entries.get(LOG.entries.size() - 1);
    }

    public static void add(String message) {

        LOG.entries.add(new LogEntry(message));
        LOG.setChanged();
        LOG.notifyObservers();
    }
}


class LogEntry {

    private String message;
    private String addInfo;

    public LogEntry(String message) {

        StackTraceElement ste = Thread.currentThread().getStackTrace()[3];
        String className = ""; //ste.getClassName().split("\\.")[1]; XXX
        String methodName = ste.getMethodName();
        String line = "" + ste.getLineNumber();
        String time = new SimpleDateFormat("HH:mm:ss").format(new Date());
        this.addInfo = time + " " + className + ":" + methodName + "(" + line + ")";
        this.message = message;
    }

    public String getMessage() {

        return message;
    }

    public String getAddInfo() {

        return addInfo;
    }
}

class LogConsole extends JFrame implements Observer {

    private final int X_DIM = 250;
    private final int Y_DIM = 200;
    private JEditorPane editor;
    private JScrollPane scroll;

    public LogConsole() {

        setTitle("LogConsole");
        setResizable(false);
        setAlwaysOnTop(true);
        setPreferredSize(new Dimension(X_DIM, Y_DIM));
        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
        setUpScroll(setUpEditor());
        pack();
        setLocationByTray();
        setVisible(true);
    }

    private Component setUpEditor() {

        editor = new JEditorPane();
        editor.setEditable(false);
        editor.setFont(new Font("Courier",Font.PLAIN,11));
        editor.setBackground(Color.black);
        editor.setForeground(Color.green);
        return editor;
    }

    private void setUpScroll(Component editor) {

        scroll = new JScrollPane();
        scroll.setViewportView(editor);
        this.add(scroll);
    }

    private void setLocationByTray() {

        int width, height;
        Rectangle visibleDesktop =
                GraphicsEnvironment.getLocalGraphicsEnvironment().
                getMaximumWindowBounds();
        width = (int) visibleDesktop.getWidth();
        height = (int) visibleDesktop.getHeight();
        setLocation(width - X_DIM, height - Y_DIM);
    }

    public void update(Observable o, Object arg) {

        Log log = (Log) o;
        String previous = editor.getText();
        String next =   log.getLatest().getAddInfo()
                        + "\n" +
                        log.getLatest().getMessage();
        editor.setText(previous + insertNewLine() + next);
        editor.setCaretPosition(editor.getText().length());
    }

    private String insertNewLine() {

        if (editor.getText().equals(""))
        return "";
        return "\n";
    }
}
 

0x7F800000

Top Contributor
hdi hat gesagt.:
Hm, also ich war mir nicht bewusst dass ich hier unnötige Wrapper-Klassen baue. Eigentlich dachte ich bisher,
dass es einigermassen professionell ist, wie ich es mache.

Ich meine statt der Klasse LogEntry kann ich natürlich auch gar keine extra Klasse machen, und einfach
direkt in der Konsole mit ArrayList<String> arbeiten.
Aber LogEntry ist nun mal nicht einfach ein String, sondern ein spezieller String der noch merh bietet. OO heisst doch auch, dass man abstrahiert und Klassen baut, damit die ÜBersicht gewährleistet wird.

Und Log und Console - so dachte/denke ich - ist auch gut zu trennen, denn das eine sind interne Daten, das andre ist die Darstellung dieser Daten. Deshalb hab ich 2 Klassen dafür, und nicht eine die das zusammenschmeisst.

So macht man das doch richtig oder?
Jajaja, okay, sry, also, die "LogConsole" finde ich zwar nach wie vor ein wenig zuuu spezialisiert mit zu wenig neuen fähigkeiten, aber sonst hast du im prinzip recht.

Nur addElement() hatte ich implementiert (das war geraten), und dann wurden die Anfangs-Einträge auch angezeigt, aber während der Laufzeit keine neuen...
Hast du auch dran gedacht, der aussenwelt von irgendwelchen Änderungen zu erzählen? Wenn du in die "innere" liste irgendwas hinzufügst, musst du doch irgendein ereignis abfeuern, sonst hört's ja kein listener, und zeichnet dementsprechnend nichts neu.

Und das zweite ist halt noch immer das Problem, dass ich eineKlasse suche, die die LogNachrichten im Fenster
schön anzeigen kann. TextArea kann nix! HAt das JList schon alles? Kann ich da Farben einstellen etc?
Jau, eigenen CellRenderer implementieren oder sowas, aber das ist auch geraten... :p

Puh.... also Java kann hart sein :p Es gibt einfach so viele fertige Dinge, die man nehmen soll, aber is schon schwer zu verstehen wenn man sie nicht selber gemacht hat.
große macht = viel verantwortung oder so :roll:
Es gibt eben verdammt viele sachen, die leute herstellen wollen, da braucht man sich nicht zu wundern dass der Baukasten nicht allzu übersichtlich ist...

Und bei so interface-geschichten ist es besser, wenn man erstmal im detail nachvollzieht, wozu die ganzen methoden da sind, und dann einige wenige rauslässt, wenn man sicher ist, dass man die nicht braucht... Meistens sind die Namen eigentlich schon eindeutig, da kann man auch mit der API auskommen, ansonsten googln oder hier kurz und knapp fragen.

kurz und knapp ist übrigens wichtig, ich glaub die leute lassen sich von solchen posts wie dem letzten ganz gut abschrecken :)

Beispielcode ist gut, roman drumrum schon bisschen gruseliger ;) wir wissn ja auch so dass swing nicht der einfachste brocken ist :p
 

hdi

Top Contributor
Danke für die Bemühungen!

Also ich hab jetzt Log von DefaultListModel erben lassen, und wieder eine JList erstellt der ich im Konstruktor diesen Log gebe. Die statische add() Methode in der Log-Klasse habe ich entsprechend angepasst:

Code:
   public static void add(String message) {

        LOG.addElement(LOG.entries.add(new LogEntry(message)));
    }

sowie auch die getElementAt(int index) überschrieben:

Code:
   @Override
    public Object getElementAt(int index){
        return LOG.entries.get(index).getMessage();
    }

Funktioniert soweit super, das Model-Konzept habe ich noch etwas verstanden..
Jetzt muss ich noch rumbasteln an der JList, sodass er mir die weiteren Infos eines LogEntrys anzeigt wenn ich auf nen Eintrag klicke, und Farben usw. Aber das hat wohl nur noch mit JList zu tun. Da werd ich wohl nochmal ein Hardcore Rendezvous mit der API haben.

Danke nochmal! Geniales Forum echt, und toll dass mir noch keiner dumm gekommen ist obwohl jeder 2.Thread in diesem kompletten Portal von mir ist :p
 

0x7F800000

Top Contributor
hehe, du könntest die konzentration deiner threads in diesem subforum halbieren, indem du die hälfte der fragen ins AWT & Swing subforum verschiebst ;) dort wären sie sogar besser aufgehoben, möcht ich fast behaupten
 

Marco13

Top Contributor
LOG.addElement(LOG.entries.add(new LogEntry(message)));

???:L Sieht erstmal komisch aus - da werden der List ja eigentlich nur booleans hinzugefügt?!
 

hdi

Top Contributor
genau, nur Booleans werden geaddet. Das war volle Absicht, zu 100% geplant gewesen,
hab mich total gefreut als ich eine riesen Liste mit "true" Einträgen hatte, dass alles so arbeitet wie geplant :gaen:

(Habs aber hinbekommen jetzt^^)
 

hdi

Top Contributor
Äh sorry Leute... ich trau mich kaum es zu sagen, aber ich wollte das jetzt nochmal neu machen,
weil ich netbeans mit allen projekten gelöscht und eclipse installiert habe...

auf einmal geht's nicht mehr, ich hab wie gesagt den alten code nicht mehr leider, und ich wüsste jetzt nicht,
was ich diesmal vergessen hab..

das problem ist, dass nur die erste nachricht geloggt wird, und weitere werden nicht in der jlist angezeigt.
Die ganze JList ist irgendwie komishc, ich kann auch den einen Eintrag nicht anwählen also markieren...

Code:
public class Main{

        public static void main(String[] args){

               new Log().start();
               // Thread, der NAchrichten addet

Code:
public class Log extends DefaultListModel{
	
	private static final Log LOG = new Log();
	private ArrayList<LogEntry> entries;

	public Log(){
		entries = new ArrayList<LogEntry>();
	}
	public void start() {
		new LogConsole(LOG).setVisible(true);
	}
	
	@Override
	public void addElement(Object obj) {
		entries.add(new LogEntry((String)obj));
	}
	@Override
	public Object getElementAt(int index) {
		return LOG.entries.get(index).getMessage();
	}
	@Override
	public int getSize() {
		return entries.size();
	}
	
	public static void add(final String message) {
		LOG.addElement(message);
	}
}

Code:
public class LogConsole extends JFrame{
	
	public LogConsole(Log log){
		
		setPreferredSize(new Dimension(200,200));
		JList list = new JList(log);
		getContentPane().add(list);
		pack();
	}
}

dachte echt, ich habs genauso gemacht wie ich es davor hatte... Verstehe wieder nicht, warum es nicht geht.
 

Marco13

Top Contributor
Hm. Warum erbt Log eigentlich von DefaultListModel? Also, irgendwie erkenne ich da keine Systematik.... Eigentlich hatte ich weiter oben ja schon ein komplettes, compilierbares Beispiel gepostet (mit dem Zaunpfahl wink).

Du willst doch von jeder beliebigen Stelle aus sowas wie
Log.add("Hallo");
aufrufen. Und das soll dann entweder in einem Frame erscheinen, oder in einer Liste, oder auf der Console, oder sonstwas. Es gibt erstmal keinen Grund, warum das was mit einem DefaultListModel zu tun haben sollte....
 

hdi

Top Contributor
hey, ja für dein beispiel bin ich auch sehr dankbar und es war hilfreich ;)

es macht schon sinn dass ich von DefaultListModel erbe, denn ich verwende zur Anzeige des Logs in meinem
Fenster eine JList. Und die JList synchronisiert sich auf diesem Modell des Logs:

Code:
new JList(log); 
// bedeutet ja:
// JList liste = new JList();
// liste.setModel(log);

Deine Variante ist halt mit Observern, so hatte ich es anfangs auch angefangen, bis man mir hier
gesagt hat, dass JList und ein Model diese Idee des Observers von Haus aus implementiert haben.

Irgendwie weiss ich echt nicht warum es nicht geht, dachte so hatte ich es vorher auch.
Da fehlt wohl was oder so ???:L Die JList aktualisiert sich nicht, oder die Anzeige aktualisiert sich nicht.
In beiden Fällen wüsste ich nicht, wieso.
 

Marco13

Top Contributor
Ja, ListModel und JList sind EINE "Instanz" des Model-View-Controller-Patterns. Trotzdem kann es sinnvoll sein, sich sein eigenes Datenmodell zu schreiben. Wenn du jetzt "Log" von "DefaultListModel" erben läßt, ist das ziemlich sinnlos: Erstens speicherst du den Inhalt ja sowieso selbst, d.h. du könntest auch von AbstractListModel erben, oder ListModel direkt implementieren, aber zweitens (wichtiger: ) Es gibt ja Fälle, in denen die Log-Ausgaben NICHT in einer JList dargestellt werden sollen. Du willst ja nicht sowas universelles wie einen Logger an sowas hyperspezielles wie eine JList koppeln.

Mit leuchtet der Sinn nicht ganz ein, warum die Log-Ausgaben in einer ArrayList gespeichert werden und so. Also, eigentlich ist ein Logger ja nur dazu da, Daten weiterzureichen - an die Konsole, in eine Datei, oder in eine JList. Aber wenn du die Daten sammeln und das ganze über MVC lösen willst, dann überleg' dir, was du machen willst.
Du willst doch sowas wie
Log.log("Hallo");
an jeder beliebigen Stelle aufrufen können. Und du willst möglichst flexibel sein, in bezug auf die Frage, WO und WIE das ganze angezeigt wird. Demnach spricht doch nichts gegen etwas, was GROB diese Struktur haben könnte:

Code:
class Log 
{
    Enthält ein "LogModel"
    Bietet eine statische "log(String message)"-Methode an, die LogEntries bastelt und ins LogModel legt
    Bietet ggf. statische Methoden an, um dem LogModel LogListener hinzuzufügen
}

class LogModel
{
    Hat Methoden zum Hinzufügen und Entfernen von LogListenern
    Hat eine Methode zum Hinzufügen eines LogEntries
        Jedes mal, wenn ein LogEntry hinzugefügt wird, werden alle LogListener benachrichtigt
}

interface LogListener
{
    Hat eine Methode "entryAdded(LogEntry)" die vom LogModel aufgerufen wird, wenn ihm ein LogEntry hinzugefügt wurde
}

Das ist dann erstmal "self-contained" - hat nichts mit JList oder ListModeln zu tun. Trotzdem kann man dann schon sowas machen wie
Log.log("Hallo");
und das funktioniert (Es macht erstmal nichts, weil es noch keine LogListener-Implementierung gibt, aber das ist egal: Es funktioniert)


Trennlinie
========================================================================

So. Der Logging-Mechanismus steht. Fertig. So weit.

Noch ne Trennlinie
========================================================================


Du weißt also, dass immer, wenn irgendwo irgendjemand eine Log-Ausgabe mach, die Methode im "entryAdded" bei allen LogListenern aufgerufen wird, die am LogModel hängen.

Jetzt muss man also nurnoch einen LogListener erstellen, und den an den Logger (bzw. an das LogModel) übergeben. Also erstellst du eine Klasse, die das LogListener interface implementiert
Code:
class SimpleLogListener implements LogListener
{
    public void entryAdded(LogEntry entry)
    {
        System.out.println("Log-Eintrag hinzugefügt: "+entry.getMessage());
    }
}
und fügst die dem Logger (bzw. dem Model) hinzu
Code:
LogListener logListener = new SimpleLogListener();
Log.addLogListenerToLogModel(logListener);

Wenn du dein Programm dann startest, werden alle Log-Meldungen auf der Console ausgegeben.

Jetzt willst du aber (zusätzlich oder optional) auch was einer JList anzeigen. Also erstellst du eine ANDERE Implementierung von LogListener. Nämlich ungefähr sowas
Code:
public class ListLogConsole extends JFrame implements LogListener
{
   private DefaultListModel model;

   public ListLogConsole()
   {
      setPreferredSize(new Dimension(200,200));
      model = new DefaultListModel();
      JList list = new JList(model);
      getContentPane().add(list);
      pack();
   }

    public void entryAdded(LogEntry entry)
    {
        model.addElement(entry);
    }
}

Die kannst du dann auch dem Logger (bzw. dem Model) hinzufügen:
Code:
LogListener logListener = new ListLogListener();
Log.addLogListenerToLogModel(logListener);

Und jedes mal, wenn eine Log-Ausgabe gemacht wird, wird diese Log-Ausgabe zur JList hinzugefügt.

(Die Ausgabe wird eigentlich dem ListModel hinzugefügt, und das verwendet MFC, um der JList bescheid zu sagen, dass sich das Model geändert hat, und die Liste aktualisiert sich - aber das ist in dem Fall egal: Davon kriegst du bei der Implementierung der ListLogConsole nichts mit, und (das ist das wichtigste) schon garnicht bei der Implementierung des eigentlichen Loggers!)

Erweiterungsmöglichkeiten wären dann z.B. noch ein "FileLogListener implements LogListener", der alle LogEntries in eine Datei schreibt, oder, oder oder.

Aber am eigentlichen Logger ändert sich nichts. Es geht dann nurnoch darum, welche LogListener hinzugefügt werden, und wie in denen diese eine, unscheinbare Methode "entryAdded" implementiert ist.
 

hdi

Top Contributor
hm okay, deine implementierung ist natürlich dynamischer. Aber bis auf die Tatsache, dass ich keine LogListener benutze, sondern quasi "hart" gecodet nur einen speziellen Fall habe, ist mein Programm ja genau so, wie von dir beschrieben.
Die Klassen die du oben erwähnt hast, sind und machen genau das.

Das Problem hier gerade liegt in der Visualisierung, so scheint es zumindest, bin mir da aber nich so sicher.
Ich hab rumgetestet: Die Einträge sind schon vorhanden, die addElement() Methode (das ist ja das, was in deinem Beispiel so eine entryAdded() Methode sein würde) wird auch immer aufgerufen.
Ich wollte jetz nach dem Adden von ein paar NAchrichten den Conten der Liste überprüfen, hab aber keine Methode
gefunden, die die Elemente zurückgibt in der Liste Oo.

Und...vllt auch komisch:
Ich mache ja
Code:
liste.setModel(log);

aber wenn ich mir ausprinten lasse:
Code:
liste.getModel();
kommt raus:

Ich weiss ja nicht ob das so normal ist...

Ich hab übrigens meinen alten Code doch noch finden können. Ich hab dort alles ganz genau so gemacht, der einzige Unterschied ist folgender (so mach ich's jetzt):

Code:
public class Log{

	private static LogListModel model;
	private static LogConsole console;

	public static void start() {

		model = new LogListModel();
		console = new LogConsole(model);
		console.setVisible(true);
	}

	public static void say(String message) {
		model.addElement(message);
	}

}

in der main-methode mach ich dann einfach:
Log.start();
und dann zB Log.say("Hallo")

is das vllt das Problem, dass model und console statische Elemente sind? Oder hab ich da sonst irgendwie etwas
verplant? Eig. muss es ja daran liegen, weil der Rest ist echt gleich... Ich hab das halt jetzt so gemacht, weil ich
die alte Klasse "Log" n bisschen hässlich und unintuitiv fand, also diese Sache dass in den Instanzvariablen
schon irgendwie eine Instanz erstellt wurde, und dass dann die add() Methode des Logs mit dieser Instanz gearbeitet hat, usw.


PS: Nicht dass du mich falsch verstehst: Ich nehm mir deinen Rat schon zu Herzen, und ich werde mir das genauer ansehen, wenn ich verstanden habe, was hier grad bei mir faul ist. Weil ich's nicht verstehe, warum es so nicht geht..
 

hdi

Top Contributor
PROBLEM GELÖST!

Sorry, ich dachte ich hab alles gleich gemacht, hab aber eine kleine Stelle anders gehabt.
Der kleine aber feine Unterschied ist folgender:

Code:
@Override
	public void addElement(Object o) {
		entries.add(new LogEntry((String) o));
	}
geht nicht, richtig heisst es:

Code:
	@Override
	public void addElement(Object o) {
		super.addElement(entries.add(new LogEntry((String) o)));
	}

War mir nicht so ganz klar, ich musste auch jetzt nochmal überlegen, warum genau.
Die Antwort ist, dass ich ja nicht weiss was addElement macht, und ich hab die Methode einfach überschrieben
und tausend Sachen weggelassen, die diese Methode normalerweise tut ^^

oh man, entschuldige nochmal für alle umstände und für eure bemühungen.
für mich gilt:

Code:
for (int age = 0 ; age < 100; age++){
:### :### :### :### :### :### :### :### :### :### :### :###
Code:
}
 

Marco13

Top Contributor
Die Antwort ist, dass ich ja nicht weiss was addElement macht, und ich hab die Methode einfach überschrieben
und tausend Sachen weggelassen, die diese Methode normalerweise tut ^^


Eine dieser 1000 Sachen ist eben: Sie benachrichtigt alle ListDataListener, die zu dem (Default)ListModel hinzugefügt wurden. Und EINER Dieser ListDataListener ist eben der, der die JList mit dem geänderten Inhalt des ListModels aktualisiert.... Kann man sich auch im Code vom DefaultListModel ansehen
Code:
    /**
     * Adds the specified component to the end of this list. 
     *
     * @param   obj   the component to be added
     * @see Vector#addElement(Object)
     */
    public void addElement(Object obj) {
	int index = delegate.size();
	delegate.addElement(obj);
	fireIntervalAdded(this, index, index); // <----------------- Da werden die Events an die ListDataListener geworfen
    }
 

hdi

Top Contributor
Jo, das hab ich mir gemerkt, ab jetzt schaue ich mir die Original Methode immer an bevor ich sie überschreibe,
oder ruf sie zumindest per super auf, wenn ich nur was zusätzlich machen will..
 
Status
Nicht offen für weitere Antworten.
Ähnliche Java Themen
  Titel Forum Antworten Datum
hdi Fehler beim Model-Update einer JList (DefaultListModel) Java Basics - Anfänger-Themen 3
Mady Daten von JList & Combobox in JTable adden Java Basics - Anfänger-Themen 2
U JList erstellen Java Basics - Anfänger-Themen 2
Soloeco JList/DefaultListModel: Wie kann ich ein Panel hinzufügen? Java Basics - Anfänger-Themen 1
J Objekt bei Auswahl in jList ändern Java Basics - Anfänger-Themen 6
D JList ListSelectionEvent entfernen? Java Basics - Anfänger-Themen 13
F JList Elemente mit Strings vergleichen Java Basics - Anfänger-Themen 12
S Export aus JList Java Basics - Anfänger-Themen 6
M Erste Schritte JList einträge Java Basics - Anfänger-Themen 1
M Erste Schritte Doppelte Ausgabe? (JList) Java Basics - Anfänger-Themen 1
M Erste Schritte Auswahl in einer JList Java Basics - Anfänger-Themen 2
P JList, aus selectedValue bestimmten Wert aus Containerklasse auslesen Java Basics - Anfänger-Themen 4
N JList + DefaultListModel + JScrollPane --> ensureIndexIsVisible funktioniert nicht immer Java Basics - Anfänger-Themen 1
C JList Einträge nach Datum sortieren Java Basics - Anfänger-Themen 3
J Element zu jList hinzufügen NullPointerExcepetion Java Basics - Anfänger-Themen 2
S jList --> Array einfügen und Liste löschen Java Basics - Anfänger-Themen 5
H Kein Zugriff auf das Element einer JList möglich: Fehlermeldung Java Basics - Anfänger-Themen 2
I in listFiles() oder JList Dateiendungen entfernen Java Basics - Anfänger-Themen 14
N Erste Schritte MySQL Tabelle in JList darstellen Java Basics - Anfänger-Themen 1
O JList aktualisieren während Dateieinlesung Java Basics - Anfänger-Themen 4
B JFileChooser und JList Java Basics - Anfänger-Themen 7
OnDemand Enumeration <> JList Java Basics - Anfänger-Themen 5
P Mehrfachauswahl in einer JList Java Basics - Anfänger-Themen 2
A JList Elemente in ein andres JList Adden Java Basics - Anfänger-Themen 5
C .txt und Jlist Java Basics - Anfänger-Themen 10
J Daten in eine JList einfügen Java Basics - Anfänger-Themen 6
A JList / toString modify Java Basics - Anfänger-Themen 2
K jlist an text anpassen Java Basics - Anfänger-Themen 3
D Erste Schritte JList + Checkbox Java Basics - Anfänger-Themen 5
I kein zugriff auf jList oder Textfield Java Basics - Anfänger-Themen 2
A JList bearbeiten Java Basics - Anfänger-Themen 2
M Index einer JList ausgeben Java Basics - Anfänger-Themen 4
M Inhalt/Wert aus einer JList ablesen Java Basics - Anfänger-Themen 5
J Markierte Einträge (Dateien) in JList sollen in einen anderen Ordner verschoben werden. Java Basics - Anfänger-Themen 12
J Markierte Items in einer JLIST in einen Ordner verschieben Java Basics - Anfänger-Themen 2
A Input/Output Hashmap in einem JPanel via JList anzeigen Java Basics - Anfänger-Themen 8
S ListModel - Anzeige im JList Java Basics - Anfänger-Themen 4
H JList Java Basics - Anfänger-Themen 2
G JList Objekte richtig anzeigen in JDK 6 Java Basics - Anfänger-Themen 5
U JList Java Basics - Anfänger-Themen 6
L HashMap zu JList Java Basics - Anfänger-Themen 6
C Input/Output Inhalte von ArrayList und JList in Datei schreiben Java Basics - Anfänger-Themen 5
M Jlist Elemente hinzufügen Java Basics - Anfänger-Themen 2
F Koordinaten JList darstellen Java Basics - Anfänger-Themen 4
M BeanBinding Jlist an Jlabel Java Basics - Anfänger-Themen 2
M GUI JList - Objekte listen u. Feld anzeigen? Java Basics - Anfänger-Themen 16
E JList GUI aktualisieren? Java Basics - Anfänger-Themen 3
JAVAnnik JList Auswahl Java Basics - Anfänger-Themen 2
Z In JList per Doppelklick eine Datei öffnen (z.B. ein PDF) Java Basics - Anfänger-Themen 16
P Jlist + Popupmenu Java Basics - Anfänger-Themen 10
M Properties Eintrag löschen, welcher in der JList "ausgewählt" wurde Java Basics - Anfänger-Themen 2
c_sidi90 File Array an Jlist übergeben Java Basics - Anfänger-Themen 11
J JList Inhalt nach JButton Klick aktualisieren Java Basics - Anfänger-Themen 8
B JList und JTable: leere Zeilen bei listfiles(); Java Basics - Anfänger-Themen 5
N DefaultListModel auf JList sortieren Java Basics - Anfänger-Themen 7
A JList mit Überschrift Java Basics - Anfänger-Themen 2
K Scrollbalken in JList funktioniert nicht Java Basics - Anfänger-Themen 9
B JList wird nicht aktualisiert bzw hat keine Items Java Basics - Anfänger-Themen 2
D JList nicht sichtbar Java Basics - Anfänger-Themen 4
B JList mit Scrollbar? Java Basics - Anfänger-Themen 7
K Datenbindung an JTable, JList, Primärschlüssel verstecken Java Basics - Anfänger-Themen 4
M JList Event Java Basics - Anfänger-Themen 5
M JList Index selektieren ausschalten. Java Basics - Anfänger-Themen 7
M ArrayList<int[]> - Problem mit JList! Java Basics - Anfänger-Themen 27
M JList + ListModel Java Basics - Anfänger-Themen 26
L JList Aktualisieren funktioniert nur Zufällig Java Basics - Anfänger-Themen 3
T JList aktualisieren Java Basics - Anfänger-Themen 12
T alle Ordner in einem Ordner in JList anzeigen Java Basics - Anfänger-Themen 3
R .Jpg anzeigen lassen nach klick in JList Java Basics - Anfänger-Themen 11
F Hilfe! JList Inhalte vertauschen Java Basics - Anfänger-Themen 2
A Eintrag einer JLIST speichern Java Basics - Anfänger-Themen 3
A Anordnung von JList Elementen Java Basics - Anfänger-Themen 3
B JList wird nach Ändern weiß Java Basics - Anfänger-Themen 11
B Einträge im JList einfügen Java Basics - Anfänger-Themen 9
S JList mit icons UND strings für Dialogbox Java Basics - Anfänger-Themen 2
DStrohma Index einer markierten Zeile aus einer JList bekommen? Java Basics - Anfänger-Themen 5
P JList in JPanel anzeigen Java Basics - Anfänger-Themen 2
hdi Synchronisation zwischen JList und ListModel Java Basics - Anfänger-Themen 6
D Suche in JList nach dem ersten Buchstaben Java Basics - Anfänger-Themen 2
1 JList Problem : Synchronisation mit Vector klappt nicht :( Java Basics - Anfänger-Themen 6
G Elemente aus jList entferne. Java Basics - Anfänger-Themen 2
G JList updaten Java Basics - Anfänger-Themen 7
K jList scrollPane Java Basics - Anfänger-Themen 2
7 Java Swing: Wie JScrollPane zur JList hinzufügen? Java Basics - Anfänger-Themen 12
K JList verschiebt sich Java Basics - Anfänger-Themen 7
G String in JList Java Basics - Anfänger-Themen 11
T Jlist Object hinzufügen Java Basics - Anfänger-Themen 2
B Auslesen der JList Java Basics - Anfänger-Themen 11
S jList Multiple Selection mit Klick Java Basics - Anfänger-Themen 2
M Dateien in einem Ordner, JList Java Basics - Anfänger-Themen 7
G JList größe verändert sich Java Basics - Anfänger-Themen 2
M JList Parameter verschieben Java Basics - Anfänger-Themen 3
C jList - Zeile Markieren - jList.setSelectedIndex(wert); Java Basics - Anfänger-Themen 3
G JList mit LinkedList füllen Java Basics - Anfänger-Themen 2
G JList Mehrfachselektion Java Basics - Anfänger-Themen 6
N 2 spaltige JList, mit dynamischen Inhalt Java Basics - Anfänger-Themen 4
P JList mit setListData(<Vector>) Java Basics - Anfänger-Themen 7
J GUI mit JList Java Basics - Anfänger-Themen 2
N drag and drop mit JList Java Basics - Anfänger-Themen 5
G 1. Buchstabe eines JList eintrages verschieden farbig machen Java Basics - Anfänger-Themen 12

Ähnliche Java Themen

Neue Themen


Oben