PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : JTree.updateUI() -> "AWT-EventQueue-0" java.lang.NullPointerException



sierra
24-01-2006, 13:28
hi,

ich habe einen JTree, der auf einem Treemodel aufbaut.
Nun habe ich von der Datenstruktur her lediglich Vektoren, die ich entsprechend in mein TreeModel mit einer vectorToTree-Methode einpflege, und dieses dann dem JTree bei der Instantiierung übergebe.

sobald ich merke, dass meine Daten im Vektor sich geändert haben, rufe ich vectorToTree erneut auf, und fülle NEU das TreeModel auf, (sind nicht soviele Elemente, so dass ich nicht darauf achte, welches Element hinzukam).

damit nun aber das JTree die Änderung für die Darstellung bemerkt, stehen mir lediglich meinJTree.repaint() und meinJTree.updateUI() zur Verfügung.

In der Regel wird daraufhin mein Baum bzw JTree dann neu dargestellt, jedoch in nicht nachvollziehbaren Umständen,
entweder der Baum war noch nicht gefüllt und wird zum ersten Mal gezeichnet, der Baum wurde zum x-ten Mal neu gezeichnet oder aber es klappt alles ohne Probleme, bekomme ich folgende Meldung, die ich nicht zurückverfolgen kann:

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at javax.swing.plaf.basic.BasicTreeUI.paintRow(Unknow n Source)
at javax.swing.plaf.basic.BasicTreeUI.paint(Unknown Source)
at javax.swing.plaf.metal.MetalTreeUI.paint(Unknown Source)
at javax.swing.plaf.ComponentUI.update(Unknown Source)
at javax.swing.JComponent.paintComponent(Unknown Source)
at javax.swing.JComponent.paint(Unknown Source)
at javax.swing.JComponent.paintWithOffscreenBuffer(Un known Source)
at javax.swing.JComponent.paintDoubleBuffered(Unknown Source)
at javax.swing.JComponent._paintImmediately(Unknown Source)
at javax.swing.JComponent.paintImmediately(Unknown Source)
at javax.swing.RepaintManager.paintDirtyRegions(Unkno wn Source)
at javax.swing.SystemEventQueueUtilities$ComponentWor kRequest.run(Unknown Source)
at java.awt.event.InvocationEvent.dispatch(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForHierar chy(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarch y(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)

Bin für jeden Hinweis dankbar....
Sierra

McFraggle
25-01-2006, 10:24
Hi sierra,

ich habe nicht ganz verstanden welche Methode nun wie wo genutzt wird, aber vielleicht hilft Dir was Grundsätzliches weiter:

Also die updateUI sollte für diesen Zweck die falsche Routine sein. "invalidate()" wäre hier etwas verständlicher (markiert die Komponente und die Eltern als "neu zu layouten"), aber auch diese Methode ist hier zweifelhaft. Diese Methoden, die direkt mit dem UI zu tun haben, sind für andere Zwecke gedacht (Look & Feel, Layout, aktives Zeichnen der Komponente).

repaint hört sich besser an, ist aber auch eine eher uneleganter Weg. Hier veranlasst man eben das Neuzeichnen der (potenziell) gesamten Komponentenfläche zu einem baldigen Zeitpunkt. Zu Beachten ist, dass diese Methode nicht sofort ein Neuzeichnen veranlasst, sondern den Wunsch dazu dem zentralen Repaint-Manager mitteilt. Dieser ruft dann irgendwann die paintImmediately der Komponente auf, die dann dem Elternelement bescheid sagt, es solle sich jetzt neu zeichnen. Dieses ruft dann wiederum (nachdem es für seine eigene darstellung gesorgt hat) die paint-Methode Deiner Komponente auf. paint ruft dann paintComponent, paintBorder und paintChildreen (in dieser Reihenfolge) auf, wobei paintComponent dann die update-Methode des entsprechenden UI-Objektes aufruft, welche den Hintergrund der Komponente zeichnet und dann paint aus dem UI-Objekt aufruft, welche die eigentliche Komponente zeichnet.
Dieser Zeichen-Mechanismus ist sehr ausgeklügelt, aber kritisch, wenn man einfach darin herum pantscht. Zudem kann die Tatsache, dass die paint-Wünsche über eine nebenläufige Queue (RepaintManager) gehen, scheinbar nicht-deterministische Erscheinungen hervorrufen. Besonders weil mehrere repaint-Aufrufe zu einem zusammengefasst werden können.

Und nun zu dem Ansatz den ich verfolgen würde, wenn ich ein Problem hätte, welches nach meinem Verständnis Deinem gleicht:
Wenn ich das richtig verstehe, hat sich Dein Modell geändert, was nun eine neue Visualisierung der Komponente nötig macht, die aber nicht automatisch erfolgt. (Warum habe ich ehrlich gesagt nicht kapiert.)
Da der JTree ja auch eine Bohne ist, sollte er eigentlich das PropertyChangeListener-Interface implementieren und durch die "propertyChange(PropertyChangeEvent)"-Methode auf eine Änderung im Datenmodell horchen. Ich würde mal schauen, ob ein einfaches Auslösen dieser Methode reicht, um dem tree klar zu machen, was passiert ist. Ansonsten mal die PropertyChangeEvent-Objekte untersuchen, die normalerweise bei einem Ändern des Datenbestandes ausgelöst werden.

Nach meiner Meinung sollte das (sehr ausgeklügelte) Swing-Paket eine elegante Möglichkeit für so was anbieten. In Javas OO-Kalkül sollte man dem tree also nicht abstrakt befehlen müssen "Neuzeichnen!", sondern ihn darüber informieren, dass sich Daten geändert haben. Daraufhin sollte er selbst die Konsequenzen ziehen. Daher mein Ansatz des property-change-Kram...


Wie gesagt, ich habe Dein Problem nicht wirklich verstanden und selber keine wirklichen Erfahrung mit dem JTree, aber ich hoffe, es war etwas brauchbares dabei...

Lin728
25-01-2006, 15:42
Nach meiner Meinung sollte das (sehr ausgeklügelte) Swing-Paket eine elegante Möglichkeit für so was anbieten. In Javas OO-Kalkül sollte man dem tree also nicht abstrakt befehlen müssen "Neuzeichnen!", sondern ihn darüber informieren, dass sich Daten geändert haben. Daraufhin sollte er selbst die Konsequenzen ziehen. Daher mein Ansatz des property-change-Kram...


Ein validate() reicht, daraufhin wird auch neu gezeichnet.

Das Problem daran ist, dass der Baum wissen muss wann genau er sich neu zeichnen soll oder Berechnungen durchzuführen.
Würde er dies nach jeder Änderung durchführen wäre das bei großen Datenmengen zu langsam (-> dann heists schon wieder java ist langsam) oder alle 500ms wäre es oft nicht ausreichend.

McFraggle
25-01-2006, 15:57
Hm, schon, aber wegen dieser Geschichte meinte ich doch, dass man das Swing-Konzept nicht durch solche Aktionen untergraben sollte.
Teilt man einer einer J-Komponente mit, dass sich das Modell geändert hat, ruft sie doch selbst eine der vier überladenen Versionen von repaint auf. (Von denen zwei eine zeitliche Verzögerung realisieren können.) Die Aufforderung geht an den Repaint-Manager, der zeitlich nah bei einander stehende paint-Aufforderungen zusammenfasst. Erst wenn der RepaintManager meint, es seie nun mal wirklich Zeit zum Neuzeichen veranlasst er dieses (mit den sparsamsten "clip bounds".
Die Komponente (hier JTree) sollte gute Bounds und Verzögerungen selbst bestimmen, wenn nötig. Der RepaintManager erledigt den restlichen Performanz-Kram durch das zeitliche Überwachen der paint-Anforderungen.

Damit ist doch eine Mitteilung wie "propertyChange" noch die beste Möglichkeit, oder nicht? Hab ich vielleicht was falsch verstanden?

Mala
08-02-2006, 17:46
Ich hatte auch das gleiche Problem wie sierra. Nach langem Herumsuchen habe ich dann rausgefunden, dass es bei mr daran lag, dass Swing nicht thread safe ist.

Also muss das updateUI() in einem eigenen thread gestartet werden, der dann per invokeLater(...) in die AWT-event-queue eingefügt werden kann. Bei mir passiert das in der treeNodesInserted(...) Methode folgendermaßen:


public void treeNodesInserted(TreeModelEvent treemodelevent)
{
EventQueue.invokeLater( new Runnable()
{
public void run() {
tree.updateUI();
} );

}

Ich hoffe, das hilft weiter.

Mala