Tip Java 93: Tambahkan aksesori pencari file ke JFileChooser

Tip ini menjelaskan cara memperluas fungsionalitas salah satu komponen antarmuka pengguna yang paling umum - dialog buka file standar - dengan aksesori pencarian file berulir.

Saat Anda mencoba membuka file tetapi tidak dapat langsung menemukannya, cukup masukkan kriteria pencarian Anda ke dalam bidang pencarian aksesori, tekan tombol Start, dan tunggu daftar file yang ditemukan muncul. Aksesori pencarian tersebut diintegrasikan ke dalam dialog file terbuka dan pencarian file dilakukan secara berantai sehingga Anda dapat terus menelusuri sistem file saat pencarian sedang berjalan.

Menambahkan fungsionalitas ke dialog file standar Swing mudah setelah Anda memahami bagaimana mengintegrasikan komponen ke dalam JFileChooserkotak dialog, bagaimana membuat komponen responsif terhadap JFileChooserkejadian, dan bagaimana mengontrol JFileChoosertampilan dan pilihan file. Saya akan memberikan contoh aksesori dengan artikel ini. Kode sumber lengkap untuk FindAccessorykelas termasuk dalam Sumber. Lihat Java Tip 85 dari Jon Sharpe untuk review JFileChooserdasar - dasarnya.

Mengakses JFileChooser

Menyesuaikan JFileChooseritu mudah. Daripada menemukan kembali dialog file standar untuk menyertakan fungsionalitas khusus, Anda dapat mengimplementasikan fungsionalitas kustom Anda sebagai JComponent dan mengintegrasikannya JFileChooserdengan satu metode panggilan.

JFileChooser chooser = new JFileChooser (); chooser.setAccessory (FindAccessory baru ());

Kedua baris kode ini tampak sederhana. Di permukaan, sebuah FindAccessorykomponen dilampirkan ke dialog buka file standar, seperti yang diilustrasikan pada Gambar 1. Pada tingkat yang lebih dalam, FindAccessorymemodifikasi perilaku JFileChooser. Detail integrasi tersembunyi di dalam implementasi aksesori.

Untuk sepenuhnya menghargai kekuatan aksesori dan fleksibilitasnya JFileChooser, Anda perlu memahami JFileChooserproperti, acara, dan metode kontrol. Namun pertama-tama, Anda harus mengetahui bagaimana komponen aksesori ditampilkan dalam JFileChooserdialog.

Mengontrol tata letak aksesori

Sangat penting untuk memahami bagaimana manajer tata letak tertentu bekerja saat menerapkan JFileChooseraksesori yang kompleks . Beberapa pengelola tata letak, seperti GridLayout, mengabaikan ukuran komponen yang disukai. Di Java 1.2.2, JFileChooserterlalu bersemangat untuk menyusutkan daftar gulir file untuk mengakomodasi aksesori. Tanpa beberapa batasan dimensi, aksesori yang kompleks dapat diperluas ke JFileChooserdaftar tampilan file dan tombol kontrol kerumunan keluar .

Untuk memperparah masalah tata letak, beberapa komponen seperti bidang teks dan daftar, cenderung diperluas untuk mengakomodasi lebar kontennya. Aturan untuk mengukur JTextFields sangat kompleks. Java Swing oleh Robert Eckstein, Marc Loy, dan Dave Wood memberikan penjelasan menyeluruh tentang ukuran bidang teks (lihat Sumberdaya).

Dalam uji coba awal dengan manajer GridLayout, FindAccessorylebar akan meluas selama pencarian untuk mengakomodasi item terluas dalam daftar hasil. Perluasan itu sering kali memangkas JFileChooserdaftar tampilan file menjadi lebar yang sangat sempit.

Untuk mengatasi masalah tata letak dan perluasan, FindAccessorygunakan pengelola BorderLayout, yang menghargai ukuran komponen yang disukai. Selain itu, panel hasil memperbaiki dimensi yang disukai dan maksimum dari daftar hasil penggulirannya tepat sebelum dimulainya pencarian.

Dimensi redup = resultsScroller.getSize (); resultsScroller.setMaximumSize (redup); resultsScroller.setPreferredSize (dim);

Memperbaiki dimensi pilihan dan maksimum terlambat atau sebelum pencarian memungkinkan FindAccessorypanel ditampilkan dengan baik saat JFileChoosermenampilkan dialognya tetapi mencegah perluasan yang tidak terkendali saat daftar hasil terisi.

Ayunan dapat meniru tampilan dan nuansa berbagai platform GUI melalui arsitektur Pluggable Look-and-Feel (PLAF). Swing 1.2.2 mencakup dukungan untuk tiga tema: Windows, Motif, dan Metal. Penampilan aksesori akan bervariasi, tergantung PLAF mana yang aktif. Anda harus menguji tata letak aksesori Anda dengan setiap PLAF.

Menanggapi acara JFileChooser

Memasang aksesori ke JFileChooser itu mudah, tetapi mengintegrasikan aksesori ke JFileChooser membutuhkan pemahaman tentang event dan pendengar perubahan properti. Aksesori dapat memantau perubahan properti dan peristiwa tindakan induknya untuk merespons aktivitas penjelajahan dan pemilihan file pengguna. Aksesori yang kompleks mungkin perlu menghentikan utas atau menutup file sementara saat pengguna mengklik tombol Buka, Simpan, atau Batal.

PropertyChangeListener

Pemroses perubahan properti sudah tidak asing lagi bagi developer JavaBeans karena mekanisme yang digunakan objek untuk memberi tahu objek lain saat nilai properti terikat berubah. Ayunan memudahkan objek untuk menerima PropertyChangeEventsdari JComponent mana pun. Cukup terapkan java.beans.PropertyChangeListenerantarmuka dan daftarkan objek Anda dengan metode komponen addPropertyChangeListener().

Aksesori yang mengimplementasikan java.beans.PropertyChangeListenerantarmuka dapat mendaftar JFileChooseruntuk menerima pemberitahuan tentang perubahan direktori, perubahan pilihan, perubahan filter file, dan banyak lagi. Lihat dokumentasi JDK untuk daftar lengkapnya.

FindAccessorymenampilkan jalur absolut dari folder root untuk pencarian Anda. Tampilan ini membeku saat menjalankan pencarian. Ketika pencarian tidak berjalan FindAccessorymemperbarui tampilan jalur pencarian sebagai respons terhadap suatu JFileChooser.DIRECTORY_CHANGED_PROPERTYperistiwa. Dengan kata lain, FindAccessorylacak pergerakan Anda melalui sistem file dengan PropertyChangeEventdari JFileChooser.

Kodenya sangat sederhana:

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

ActionListener

Aksesori yang mengimplementasikan java.awt.event.ActionListenerantarmuka dapat menerima pemberitahuan ketika Anda mengklik tombol Buka, Simpan, atau Batal.

FindAccessorymenghentikan pencarian ketika Anda mengklik tombol buka atau batal. The ActionListenerMetode sederhana:

public void actionPerformed (ActionEvent e) {String command = e.getActionCommand (); jika (perintah == null) kembali; // Bisakah ini terjadi? Mungkin tidak. Panggil aku paranoid. jika (command.equals (JFileChooser.APPROVE_SELECTION)) keluar (); lain jika (command.equals (JFileChooser.CANCEL_SELECTION)) keluar (); }

Mengontrol JFileChooser

Aksesori bisa lebih dari sekadar budak JFileChooserproperti dan acara. Itu dapat menggunakan kontrol JFileChoosersebanyak pengguna dengan keyboard dan mouse.

Saat Anda mengklik dua kali item dalam FindAccessorydaftar hasil pencarian, JFileChoosermenampilkan dan memilih item itu. FindAccessorymenggunakan JFileChoosermetode untuk menyetel direktori saat ini, untuk menyetel pilihan saat ini, dan untuk mengubah jenis file yang ditampilkan.

Berikut ini adalah kode untuk FindAccessory's goTo()metode yang perintah JFileChooseruntuk menampilkan dan pilih file ketika Anda klik dua kali item dalam daftar hasil pencarian. Prosesnya sedikit lebih rumit daripada meminta JFileChooser.setSelectedFile(). Pertama, Anda menyetel JFileChooserfilter tampilan file saat ini agar file Anda dapat ditampilkan. Kedua, Anda menyetel JFileChooserdirektori saat ini ke folder yang berisi file yang ditentukan. Akhirnya, Anda memohon 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.

Setiap FindAccessorytab pencarian menerapkan FindFilterFactoryantarmuka. Saat pencarian dimulai, FindAccessorypengontrol memutar melalui panel tab dan memanggil newSearch()setiap instance FindFilterFactoryuntuk mengambil FindFilter. Kontroler menginisialisasi mesin pencari dengan array FindFilters. Masing-masing FindFiltermenerapkan accept()metode sehingga algoritme pemilihan benar-benar tersembunyi dari mesin pencari.

Memperluas FindAccessorydengan kategori pencarian baru adalah proses tiga langkah yang mudah: