Java-Tipp 93: Fügen Sie JFileChooser ein Dateifinder-Zubehör hinzu

In diesem Tipp wird beschrieben, wie Sie die Funktionalität einer der am häufigsten verwendeten Komponenten der Benutzeroberfläche - des Standarddialogs zum Öffnen von Dateien - um ein Zubehör für die Suche nach Thread-Dateien erweitern.

Wenn Sie versuchen, eine Datei zu öffnen, diese aber nicht sofort finden können, geben Sie einfach Ihre Suchkriterien in die Suchfelder des Zubehörs ein, klicken Sie auf die Schaltfläche Start und warten Sie, bis eine Liste der gefundenen Dateien angezeigt wird. Dieses Suchzubehör ist in den Dialog zum Öffnen von Dateien integriert, und die Dateisuche ist mit einem Thread versehen, sodass Sie das Dateisystem weiterhin durchsuchen können, während die Suche ausgeführt wird.

Das Hinzufügen von Funktionen zum Standard-Dateidialog von Swing ist einfach, sobald Sie verstanden haben, wie eine Komponente in JFileChooserdas Dialogfeld integriert wird, wie die Komponente auf JFileChooserEreignisse reagiert und wie die JFileChooserDateianzeige und -auswahl der Komponente gesteuert wird . Ich werde mit diesem Artikel ein Beispielzubehör bereitstellen. Der vollständige Quellcode für die FindAccessoryKlasse ist in Ressourcen enthalten. In Jon Sharpes Java-Tipp 85 finden Sie eine Übersicht über die JFileChooserGrundlagen.

Zubehör für JFileChooser

Das Anpassen JFileChooserist einfach. Anstatt den Standarddateidialog neu zu erfinden, um spezielle Funktionen einzuschließen, können Sie Ihre benutzerdefinierten Funktionen als JComponent implementieren und in JFileChoosereinen einzelnen Methodenaufruf integrieren.

JFileChooser chooser = neuer JFileChooser (); chooser.setAccessory (neues FindAccessory ());

Diese beiden Codezeilen sind täuschend einfach. An der Oberfläche wird eine FindAccessoryKomponente an einen Standarddialog zum Öffnen von Dateien angehängt, wie in Abbildung 1 dargestellt. Auf einer tieferen Ebene FindAccessorywird das Verhalten von geändert JFileChooser. Die Details der Integration sind in der Implementierung des Zubehörs verborgen.

Um die Leistungsfähigkeit des Zubehörs und die Flexibilität des Zubehörs voll zu nutzen JFileChooser, müssen Sie die JFileChooserEigenschaften, Ereignisse und Steuerungsmethoden verstehen . Zunächst sollten Sie jedoch wissen, wie eine Zubehörkomponente im JFileChooserDialogfeld angezeigt wird .

Steuern des Zubehörlayouts

Es ist besonders wichtig zu verstehen, wie bestimmte Layout-Manager bei der Implementierung komplexer JFileChooserZubehörteile arbeiten. Einige Layout-Manager wie GridLayout ignorieren die bevorzugte Größe einer Komponente. In Java 1.2.2 JFileChooserist es zu eifrig, die Bildlaufliste der Dateien zu verkleinern, um ein Zubehör aufzunehmen. Ohne einige Dimensionsbeschränkungen kann ein komplexes Zubehör erweitert werden JFileChooser, um die Dateianzeigeliste und die Steuertasten zu verdrängen.

Um das Layout noch schlimmer zu machen, werden einige Komponenten wie Textfelder und Listen tendenziell erweitert, um der Breite ihres Inhalts Rechnung zu tragen. Die Regeln für die Größe von JTextFields sind besonders komplex. Java Swing von Robert Eckstein, Marc Loy und Dave Wood bietet eine ausführliche Erklärung der Größe von Textfeldern (siehe Ressourcen).

In frühen Versuchen mit dem GridLayout-Manager wurde FindAccessorydie Breite während einer Suche erweitert, um das breiteste Element in der Ergebnisliste aufzunehmen. Diese Erweiterung hat die Dateianzeigeliste oft JFileChooserauf eine lächerlich schmale Breite reduziert .

FindAccessoryVerwenden Sie den BorderLayout-Manager, der die bevorzugte Größe einer Komponente berücksichtigt, um Layout- und Erweiterungsprobleme zu umgehen. Darüber hinaus werden im Ergebnisbereich die bevorzugten und maximalen Abmessungen der Liste der Bildlaufergebnisse unmittelbar vor dem Start einer Suche festgelegt.

Dimension dim = resultsScroller.getSize (); resultsScroller.setMaximumSize (dim); resultsScroller.setPreferredSize (dim);

Wenn Sie die bevorzugten und maximalen Abmessungen spät oder kurz vor einer Suche festlegen, werden die FindAccessoryBedienfelder beim JFileChooserAnzeigen des Dialogfelds gut angezeigt, verhindern jedoch eine außer Kontrolle geratene Erweiterung, wenn die Ergebnisliste voll ist.

Swing kann das Erscheinungsbild verschiedener GUI-Plattformen durch seine Pluggable Look-and-Feel-Architektur (PLAF) emulieren. Swing 1.2.2 unterstützt drei Themen: Windows, Motiv und Metal. Das Erscheinungsbild des Zubehörs hängt davon ab, welches PLAF aktiv ist. Sie sollten Ihr Zubehörlayout mit jedem PLAF testen.

Antworten auf JFileChooser-Ereignisse

Das Anhängen eines Zubehörs an JFileChooser ist einfach. Die Integration eines Zubehörs in JFileChooser erfordert jedoch ein Verständnis der Listener für Ereignis- und Eigenschaftsänderungen. Ein Zubehör kann die Eigenschaftsänderungen und Aktionsereignisse seiner Eltern überwachen, um auf die Browsing- und Dateiauswahlaktivitäten des Benutzers zu reagieren. Komplexes Zubehör muss möglicherweise Threads beenden oder temporäre Dateien schließen, wenn der Benutzer auf die Schaltflächen Öffnen, Speichern oder Abbrechen klickt.

PropertyChangeListener

Listener für Eigenschaftsänderungen sind JavaBeans-Entwicklern als der Mechanismus bekannt, mit dem ein Objekt andere Objekte benachrichtigt, wenn sich ein gebundener Eigenschaftswert ändert. Mit Swing können Objekte problemlos PropertyChangeEventsvon jeder JComponent empfangen werden. Implementieren Sie einfach die java.beans.PropertyChangeListenerSchnittstelle und registrieren Sie Ihr Objekt mit der addPropertyChangeListener()Methode der Komponente .

Zubehör, das die java.beans.PropertyChangeListenerSchnittstelle implementiert , kann sich registrieren JFileChooser, um Benachrichtigungen über Verzeichnisänderungen, Auswahländerungen, Dateifilteränderungen und mehr zu erhalten. Eine vollständige Liste finden Sie in der JDK-Dokumentation.

FindAccessoryZeigt den absoluten Pfad des Stammordners für Ihre Suche an. Diese Anzeige friert beim Ausführen einer Suche ein. Wenn eine Suche nicht ausgeführt wird, wird FindAccessoryder Suchpfad als Reaktion auf ein JFileChooser.DIRECTORY_CHANGED_PROPERTYEreignis aktualisiert . Mit anderen Worten, FindAccessoryverfolgt Ihre Bewegung durch das Dateisystem mit einem PropertyChangeEventvon JFileChooser.

Der Code ist sehr einfach:

public void propertyChange (PropertyChangeEvent e) {String prop = e.getPropertyName (); if (prop.equals (JFileChooser.DIRECTORY_CHANGED_PROPERTY)) {updateSearchDirectory (); }}

ActionListener

Zubehör, das die java.awt.event.ActionListenerSchnittstelle implementiert , kann benachrichtigt werden, wenn Sie auf die Schaltflächen Öffnen, Speichern oder Abbrechen klicken.

FindAccessorystoppt eine Suche, wenn Sie auf die Schaltflächen Öffnen oder Abbrechen klicken. Die ActionListenerMethode ist einfach:

public void actionPerformed (ActionEvent e) {String command = e.getActionCommand (); if (command == null) return; // Kann das passieren? Wahrscheinlich nicht. Nenn mich paranoid. if (command.equals (JFileChooser.APPROVE_SELECTION)) quit (); sonst wenn (command.equals (JFileChooser.CANCEL_SELECTION)) quit (); }}

JFileChooser steuern

Ein Zubehör kann mehr als ein Slave für JFileChooserEigenschaften und Ereignisse sein. Es kann JFileChoosermit Tastatur und Maus so viel Kontrolle ausüben wie ein Benutzer.

Wenn Sie auf ein Element in FindAccessoryder Suchergebnisliste doppelklicken, JFileChooserwird dieses Element angezeigt und ausgewählt. FindAccessoryVerwendet JFileChooserMethoden, um das aktuelle Verzeichnis festzulegen, die aktuelle Auswahl festzulegen und den Typ der angezeigten Dateien zu ändern.

Unten finden Sie den Code für FindAccessorydie goTo()Methode, JFileChoosermit der eine Datei angezeigt und ausgewählt werden kann, wenn Sie auf ein Element in der Suchergebnisliste doppelklicken. Der Vorgang ist etwas komplizierter als das Aufrufen JFileChooser.setSelectedFile(). Zunächst legen Sie den JFileChooseraktuellen Dateianzeigefilter fest, damit Ihre Datei angezeigt werden kann. Zweitens legen Sie das JFileChooseraktuelle Verzeichnis auf den Ordner fest, der die angegebene Datei enthält. Schließlich rufen Sie auf JFileChooser.setSelectedFile().

Step 2 is only necessary if you're running a version prior to Java 1.2.2. A bug in JFileChooser.setSelectedFile() didn't always change the current directory.

/** Set parent's current directory to the parent folder of the specified file and select the specified file. That method is invoked when the user double-clicks on an item in the results list. @param f File to select in parent JFileChooser */ public void goTo (File f) { if (f == null) return; if (!f.exists()) return; if (chooser == null) return; // Make sure that files and directories // can be displayed chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); // Make sure that parent file chooser will // show the type of file specified javax.swing.filechooser.FileFilter filter = chooser.getFileFilter(); if (filter != null) { if (!filter.accept(f)) { // The current filter will not // display the specified file. // Set the file filter to the // built-in accept-all filter (*.*) javax.swing.filechooser.FileFilter all = chooser.getAcceptAllFileFilter(); chooser.setFileFilter(all); } } // Tell parent file chooser to display contents of parentFolder. // Prior to Java 1.2.2 setSelectedFile() did not set the current // directory the folder containing the file to be selected. File parentFolder = f.getParentFile(); if (parentFolder != null) chooser.setCurrentDirectory(parentFolder); // Nullify the current selection if any. // Why is this necessary? // JFileChooser gets sticky (i.e., it does not // always relinquish the current selection). // Nullifying the current selection seems to yield better results. chooser.setSelectedFile(null); // Select the file chooser.setSelectedFile(f); // Refresh file chooser display. // Is this really necessary? Testing on a variety of systems with // Java 1.2.2 suggests that helps. Sometimes it doesn't work, // but it doesn't do any harm. chooser.invalidate(); chooser.repaint(); } 

Caveats

JavaSoft's Bug Database contains 260 bug reports for JFileChooser. Of those 260 reports, 12 are related to JFileChooser.setSelectedFile(), but 10 have been fixed for JDK 1.2.2. You should make sure to run the most recent release of Java. FindAccessory has been tested with JDK 1.2.2 on Windows NT/98/95. The only known problem is JFileChooser's reluctance to display the selection when you double-click a file in the Found list. JFileChooser.setSelectedFile() selects the specified file, but the selection is not always displayed in the scrolling file list. You will see the file name displayed correctly, but the file list does not highlight it. The Open button works. Double clicking the item a second time displays the selection correctly. That bug appears to be cosmetic.

FindAccessory implementation details

FindAccessory extends JPanel and implements a threaded utility for finding files by name, date of modification, and content. FindAccessory is comprised of three components: a controller, a user interface, and a search engine. For code simplicity, the controller and the search engine are implemented within the FindAccessory class.

Search options are specified in three tab panes labeled Name, Date, and Content. Results are displayed in a fourth tab pane labeled Found. Searching is recursive from the current location (the path is displayed above the search tabbed panes). The search function is threaded so you can continue to browse the file system while a search is running. You may change the search criteria without affecting a running search.

Search results are displayed dynamically in a scrolling JList within the Found tab pane. You can double-click an entry in the results list to force JFileChooser to show and select the entry in its main scrolling view.

Search progress is displayed as a text label in the lower right corner of the accessory as number of items found/number of items searched.

FindAccessory user interface

Accessory layout varies depending on which Pluggable Look-and-Feel (PLAF) is active. Windows and Metal PLAF render JFileChooser with similar layouts and allocate comparable space for your accessory. By contrast, Motif PLAF allocates much less space for an accessory, so your components may appear scrunched. You could customize your layout for each PLAF. FindAccessory uses a 10-point Helvetica font and arranges components to use minimal space. Test your accessory with each PLAF to make sure it looks right.

FindAccessory tab panes

In addition to the find-by-name tab illustrated in Figure 1,

FindAccessory

contains find-by-date, find-by-content, and found-items tabs, as shown by Figures 2 through 4.

Finding the right files

Implementing the selection functions for a search engine can be complicated. Ideally, the search controller and the search engine should know nothing about the implementation of a search function's selection algorithm. The FindAccessory class implements a recursive search engine that uses an array of FindFilter objects to test a file's acceptance. Each FindAccessory tab pane is responsible for implementing a user interface for the search as well as a FindFilter object. The search engine and the file selection functions enjoy separate responsibilities.

Jede der FindAccessorySuchregisterkarten implementiert eine FindFilterFactorySchnittstelle. Wenn eine Suche beginnt, FindAccessorydurchläuft der Controller die Registerkartenfenster und ruft newSearch()bei jeder Instanz von FindFilterFactoryauf, um a abzurufen FindFilter. Der Controller initialisiert die Suchmaschine mit dem Array von FindFilters. Jedes FindFilterimplementiert eine accept()Methode, sodass die Auswahlalgorithmen vollständig vor der Suchmaschine verborgen sind.

Das Erweitern FindAccessoryum eine neue Suchkategorie ist ein einfacher dreistufiger Prozess: