Du verwendest einen veralteten Browser. Es ist möglich, dass diese oder andere Websites nicht korrekt angezeigt werden. Du solltest ein Upgrade durchführen oder ein alternativer Browser verwenden.
Ich möchte das sich der JTree nach dem Löschen einer Datei erneuert so das die Datei im jTree nicht mehr zu sehen ist. Wie mach ich das? Muss ich dazu den jTree neu aufrufen oder das TreeModel und wie kann ich das machen?
mein Aufbau:
Code:
private javax.swing.JTree getJTree() {
if(jTree == null) {
myFile root = new myFile(System.getProperty("file.separator"));
final FileTreeModel model = new FileTreeModel(root);
jTree = new javax.swing.JTree(model);
.
.
//hier ist noch ein selektion Listener für die Selektion im JTree der einen pathname zurück gibt
}
// mit der Methode bekomme ich den pathname und lösche das Verzeichniss samt inhalt
public static void deleteTree (File pathname)
{.....
}
model.addTreeModelListener (new TreeModelListener() {
public void treeNodesChanged(TreeModelEvent e) {}
public void treeNodesInserted(TreeModelEvent e) {}
public void treeNodesRemoved(TreeModelEvent e) { //WAS muss hier dann rein//?}
public void treeStructureChanged(TreeModelEvent e) {}
}
);
Was Du schreibst ist: "Ich muss einen Listener implementieren"
Was ich sagen wollte ist: "Du musst einen Listener aufrufen"
Der Code den Du im FileTreeModel noch hinzufügen musst, könnte etwa so aussehen:
Code:
public class FileTreeModel implements TreeModel{
private Vector listeners = new Vector();
(...) // anderer Code
// wird von aussen aufgerufen
public void addTreeModelListener( TreeModelListener x ){
listeners.add( x );
}
public void fileDeleted( File x ){
TreeModelEvent event = new TreeModelEvent( ... ) // Die Argumente in der API nachschauen, kenne sie nicht auswendig
for( int i = 0, n = listeners.size(); i<n; i++ )
((TreeModelListener)listeners.get(i)).treeNodesRemoved( event );
}
}
Sie dir doch mal den Quellcode des DefaultTreeModel (API hier) an. (besonders die Methoden die mit fireXXX beginnen)
Schau mal, wie das in diesem Tutorial gelöst wurde!
(Funktioniert nicht ganz gleich wie dein FileTreeModel, aber der Aufruf der Listeners (vorallem das wann und wo) entspricht dem, was ich dir sagen will)
Ein Ausschnitt:
Code:
public DefaultMutableTreeNode addObject(DefaultMutableTreeNode parent,
Object child,
boolean shouldBeVisible) {
DefaultMutableTreeNode childNode =
new DefaultMutableTreeNode(child);
...
//>>> an dieser Stelle wird dem JTree (auf Umwegen) gesagt, dass es einen neuen Node gibt.
//>>> treeModel ist hier ein "DefaultTreeModel". Aber es geht ja auch nur ums Prinzip
treeModel.insertNodeInto(childNode, parent, parent.getChildCount());
//Make sure the user can see the lovely new node.
if (shouldBeVisible) {
tree.scrollPathToVisible(new TreePath(childNode.getPath()));
}
return childNode;
}
Wenn nun jemand auf den Button klickt, benachrichtig der Button den Listener ( actionPerformed( ActionEvent e) ).
User klickt auf Button -> Button bemerkt dies -> Button benachrichtig Listener -> Listener arbeitet mit irgendetwas weiter. graphische Oberfläche (Button geklickt) -> innere Strukturen (Code ausführen, z.B. Datei speichern)
:arrow: Es gibt also zwei Seiten: den Button (der "Sender") und der ActionListener (der "Empfänger").
Soweit alles klar? gut, weiter geht es.
Tree und Model
Bei dem Tree und dem Model gibt es nun ebenfalls einen Sender und einen Empfänger.
Es gibt zwei Möglichkeiten: JTree = Sender, Model = Empfänger:Das hast Du bis jetzt versucht. Das funktioniert offenbar nicht. Wie auch, wie soll der Empfänger dem Sender mitteilen, dass sich etwas verändert hat?
:arrow: falsch
JTree = Empfänger, Model = Sender: Auf diese Weise kann das Model den JTree benachrichtigen, sollten Nodes hinzugefügt, Nodes entfernt, ... werden.
:arrow: richtig
Du musst erkennen: diesmal kommt das Ereignis nicht von der graphischen Oberfläche, sondern vom inneren des Programmes aus. innere Strukturen (Node hinzugefügt, ...) -> graphische Oberfläche (Baum neuzeichnen)
:arrow: Du schickst das Event ab, und Du musst nicht den Listener implementieren.
Vielleicht lösst das deinen Knopf?
Sonst melde dich.
Ok also das Model ist mein Sender und der Tree ist der Empänger. Das heisst wenn jemand ein path löscht sendet das Model und der Tree empfängt das sich die Structur geändert hat.
Ok und ich soll nicht den Listener implementieren sondern das Event abschicken. Ok Grundprinzip klar nur Umsetztung noch nicht ganz. :cry: ???:L
Dann implementieren wir mal einen Teil eines TreeModels:
(Ich gebs zu, dieser Code ist nicht getestet, aber die Richtung stimmt :wink
Beginnen wir mal mit dem einfachen Teil. Hier halten wir uns einfach an das Interface TreeModel.
Es gibt einen Konstruktor und einen Vector, der TreeModelListener's speichert (diese werden später gebraucht)
Code:
import java.io.File;
import java.util.Vector;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
public abstract class FileTreeModel implements TreeModel{
private Vector vector = new Vector();
public FileTreeModel(){
}
/**
* Speichert einen zusätzlichen TreeModelListener
* @param l Der neue TreeModelListener
*/
public void addTreeModelListener(TreeModelListener l) {
vector.add( l );
}
So, kommen wir zum Löschen einer Datei.
Die Datei löschen wir im Model selbst. Das ist nicht unbedingt notwendig, aber praktisch.
Wir gehen folgendermassen vor:
1. Position der Datei im Verzeichnisbaum finden und speichern.
2. Datei löschen
3. Sofern sie gelöscht wurde, den JTree benachrichtigen (Darum mussten wir die Position der Datei speichern).
Code:
/**
* Diese Methode löscht eine Datei und benachrichtigt anschliessend den JTree,
* das eine Datei gelöscht wurde.
* @param file Die Datei die gelöscht werden soll.
*/
public void deleteFile( File file ){
// Als erster benötigen wir die Position dieses Files im Baum
File parent = file.getParentFile();
TreePath path = createPath( parent );
int index = getIndexOfChild( parent, file );
// Jetzt die Datei löschen
boolean deleted = file.delete();
if( deleted ){
// jetzt den JTree benachrichtigen
fireNodeRemoved( path, index, file );
}
}
Da fehlt doch noch was!
Richtig, die Methode "createPath" benötigen wir noch.
Sie stellt einfach einen TreePath her, zu einer Datei die wir ihr übergeben.
Code:
/**
* Diese Methode liefert den TreePath zu dem angegebenen File.
* @param file Das File
* @return Der TreePath
*/
protected TreePath createPath( File file ){
Vector files = new Vector(); // Zwischenspeicher
// Die Parents der Reihe nach auslesen
do{
files.add( file );
file = file.getParentFile();
}while( file != null );
// Verkehrt herum in einen Array kopieren
int size = files.size();
Object[] path = new Object[ size ];
for( int i = 0; i < size; i++ )
path[i] = files.get( size - 1 - i );
// TreePath initialisieren und zurückgeben.
return new TreePath( path );
}
Jetzt brauchen wir noch eine Möglichkeit den JTree zu benachrichtigen.
Dazu schreiben wir die Methode "fireNodeRemoved". Diese Methode stellt zuerst ein TreeModelEvent her, und sendet es danach in die grosse Welt hinaus.
Code:
/**
* Benachrichtigt alle registrierten TreeModelListener, dass ein Node
* entfernt wurde.
* @param path Der Pfad zum Parent des entfernten Nodes
* @param index Der ehemalige Index des Nodes
* @param removed Der entfernte Node
*/
protected void fireNodeRemoved( TreePath path, int index, Object removed ){
// Event herstellen
TreeModelEvent event = new TreeModelEvent( this, path, new int[]{ index }, new Object[]{ removed } );
// Event verschicken
for( int i = 0, n = vector.size(); i<n; i++ )
((TreeModelListener)vector.get(i)).treeNodesRemoved( event );
}
Damit können wir Dateien löschen, und der JTree wird es bemerken. Einige Methoden fehlen noch, aber die kannst du sicher selbst implementieren.
hmm ok aber ich habe mein model etwas anders aufgebaut:
Code:
class FileTreeModel implements TreeModel {
protected myFile root; // Wurzel
public FileTreeModel (myFile root) { this.root = root; }
public void setroot (myFile pathnamer){
this.root = pathnamer;
}
public Object getRoot() { return root; } // Liefert die Wurzel des Funktionsbaumes
public boolean isLeaf(Object node) { return ((myFile)node).isFile(); } // ist es ein Blatt oder nicht
private Vector listeners = new Vector();
public int getChildCount(Object parent) { //bekommt object Eltern und soll Anzahl Kind-Knoten dieses Knotens liefern
String[] children = ((myFile)parent).list(); //gibt String Liste von parent
if (children == null) return 0; //wenn keine children dann 0
return children.length; // sonst Länge der Kinder zurückgeben
}
public Object getChild(Object parent, int index) { // bekommt parent und Index des gesuchten Kind-Knotens und liefert Object Kind-Knoten
String[] children = ((myFile)parent).list(); //gibt String Liste von parent
if ((children == null) || (index >= children.length)) return null;
return new myFile((myFile) parent, children[index]);
}
public int getIndexOfChild(Object parent, Object child) { //bekommt parent und object Kind
String[] children = ((myFile)parent).list();
if (children == null) return -1; //wenn kinder 0 dann ende -1
String childname = ((myFile)child).getName(); //String name des Kindes ist
//childname = childname.substring(0,6);
for(int i = 0; i > children.length; i++) {
if (childname.equals(children[i])) return i;
}
return -1;
}
public void valueForPathChanged(TreePath path, Object newvalue) {} // Wird aufgerufen, wenn der Wert für den Pfad geändert wurde.
public void addTreeModelListener(TreeModelListener l) {
listeners.add( l );
} //Fügt den Listener hinzu
public void removeTreeModelListener(TreeModelListener l) {} // Entfernt den Listener
}
[/code]
und mein path lösche ich indem ich bei der selektion ein path(file) bestimme und das übergeben an:
Code:
public static void deleteTree (File pathname)
{
File files [] = pathname.listFiles(); //File Array mit Wurzel pathname
for (int i = 0; i < files.length; i++) //von Anfang bis zum Ende des Arrays
{
if (files [i].isDirectory() ) //Wenn File i ein Verzeichniss ist
{deleteTree (files [i] );} // Lösche den Inhalt des Verzechniss
files [i].delete();
}
pathname.delete(); // Lösche dann noch den pathnamen(Wurzel)
}
Du musst jetzt einfach noch den JTree benachrichtigen, indem Du ein TreeModelEvent erzeugst (in der API steht, welche Argumente benötigt werden), und dann auf eine ähnliche Art wie in protected void fireNodeRemoved( TreePath path, int index, Object removed ) versendest.
Diese Methode deleteTree muss halt noch eine Verbindung zu den Models bekommen. Vielleicht speicherst Du irgendwo alle Models die etwas darstellen, so dass Du in deleteTree Zugriff hast, oder oder ... es gibt viele Varianten.