Портянкин И. Swing
.pdfСтандартные диалоговые окна |
425 |
import java.awt.*;
public class FilteringFiles extends JFrame { public FilteringFiles() {
super("FilteringFiles"); setDefaultCloseOperation(EXIT_ON_CLOSE);
//выводим окно на экран setSize(300, 200); setVisible(true);
//настраиваем компонент для выбора файла
JFileChooser chooser = new JFileChooser(); chooser.setDialogTitle("Выберите текстовый файл");
//присоединяем фильтр chooser.addChoosableFileFilter(
new TextFilesFilter());
//выводим диалоговое окно на экран
int res = chooser.showOpenDialog(this); if ( res == JFileChooser.APPROVE_OPTION )
JOptionPane.showMessageDialog(
this, chooser.getSelectedFile());
}
// фильтр, отбирающий текстовые файлы class TextFilesFilter extends FileFilter {
// принимает файл или отказывает ему public boolean accept(java.io.File file) {
// все каталоги принимаем
if ( file.isDirectory() ) return true; // для файлов смотрим на расширение
return ( file.getName().endsWith(".txt") );
}
// возвращает описание фильтра public String getDescription() {
return "Текстовые файлы (*.txt)";
}
}
public static void main(String[] args) { SwingUtilities.invokeLater(
new Runnable() {
public void run() { new FilteringFiles(); } });
}
}
426 |
ГЛАВА 14 |
Мы создаем небольшое окно, и сразу же после того как это окно появляется на экране, выводим компонент для выбора файлов JFileChooser с подключенным к нему фильтром файлов. Настройка нам уже знакома: мы просто устанавливаем заголовок диалогового окна для открытия файлов, а затем выводим это окно на экран методом showOpenDialog().
Посмотрите, как создается фильтр файлов. Мы унаследовали внутренний класс от абстрактного класса FileFilter и переопределили два метода базового класса. Метод getDescription() не таит в себе никаких тайн и должен просто сообщать краткое описание фильтра файлов. Как правило, в это описание добавляется правило, по которому действует фильтр. В нашем случае мы просто указали маску, по которой отбираются файлы. Главная «рабочая лошадка» фильтра — метод accept(). Именно в нем нам необходимо проанализировать имя файла (или даже его содержимое) и сделать вывод о его пригодности для включения в список файлов, который окажется перед глазами пользователя. Обратите внимание, что в данный метод попадают абсолютно все файлы и каталоги, поэтому мы прежде всего проверяем, не каталог ли нам попался, и все каталоги включаем в список (в противном случае, при отсутствии в списке каталогов, пользователь просто не сможет перемещаться по файловой системе). Для обычных файлов мы проводим анализ имени: наш фильтр должен отбирать текстовые файлы, а их имена (точнее расширения) заканчиваются символами «.txt».
Присоединить фильтр файлов к компоненту JFileChooser можно двумя способами. Оба способа предназначены для присоединения нового фильтра к списку фильтров, уже имеющихся в компоненте JFileChooser. В примере мы применили первый способ, вызвав метод addChoosableFileFilter(). Данный метод присоединит наш фильтр файлов к списку остальных фильтров (а в компоненте JFileChooser всегда имеется, по крайней мере, один фильтр — тот, что пропускает все файлы и каталоги «без разбора»), но не выберет новый фильтр, так что пользователю самому придется его выбирать . Есть и второй способ — вызов метода setFileFilter(). Он, так же как и метод addChoosableFileFilter(), добавит фильтр в список имеющихся фильтров, но к тому же выберет его текущим фильтром и незамедлительно отфильтрует все файлы в текущей директории, так что пользователь сразу же увидит что позволяет выбирать данный фильтр. Какой метод использовать зависит от конкретной ситуации, главным образом от того, какой фильтр вы планируете использовать по умолчанию.
Запустив программу с примером, вы увидите, что после присоединения фильтра файлов компонент JFileChooser стал отображать только текстовые файлы (после того, как фильтр выбран в списке фильтров), и выбирать среди них нужный файл стало гораздо проще. Видно, как описание, переданное нами из метода getDescription(), появляется в раскрывающемся списке доступных фильтров файлов. Фильтры файлов намного облегчают жизнь пользователя, особенно при выборе из большого количества файлов, так что стоит всегда держать их «под рукой».
Список фильтров можно не только дополнять новыми участниками, но и уменьшать и узнавать, какие фильтры доступны в данный момент. Полезные методы для работы с фильтрами изложены в следующей таблице:
Таблица 14.7. Методы для работы с фильтрами файлов в компоненте JFileChooser
Метод |
Описание |
getChoosableFileFilters() |
ВозвращаетмассиввсехдобавленныхвJFileChooser филь- |
|
тров файлов, включая фильтр по умолчанию (для выбора |
|
без ограничения) |
getFileFilter() |
Возращает фильтр, выбранный в данный момент. Полез- |
|
ный метод, иногда именно выбранный фильтр определяет, |
|
в каком формате будет сохранен файл |
Стандартные диалоговые окна |
427 |
|
Таблица 14.7 (продолжение) |
|
|
|
|
|
Метод |
Описание |
|
setAcceptAllFileFilterUsed(boolean) |
Метод позволяет указать, надо ли показывать универсаль- |
|
|
ный фильтр для выбора без ограничения. Отличная замена |
|
|
ручному удалению этого фильтра |
|
removeChoosableFileFilter(FileFilter) |
Удаляет указанный в качестве параметра фильтр из списка |
|
|
фильтров |
|
Внешний вид файлов
Возможность фильтрации файлов перед тем, как они попадут в список файлов компонента JFileChooser, и их увидит пользователь — не единственное дополнение к стандартному диалоговому окну для выбора файлов. Вы также можете настроить внешний вид файлов, отображаемых компонентом JFileChooser. Оказывается, нет ничего невозможного в том, чтобы указать, какие у файлов должны быть имена, значки и описания.
Для описания внешнего вида файлов компонент JFileChooser применяет объект специального класса FileView из пакета javax.swing.filechooser. Данный объект служит для получения исчерпывающей информации о внешнем виде отображаемого файла7. С помощью объекта FileView вы можете задать имена файлов (если вам нужно, чтобы они отличались от имен файлов в файловой системе, например вы можете хранить название документа непосредственно в файле и вместо имени файла выводить на экран это название), значки для файлов и каталогов, описания типов файлов и многое другое. Для каждого атрибута внешнего вида файла предназначен свой метод, вы наследуете от класса FileView и переопределяете необходимые вам методы.
Рассмотрим теперь небольшой пример, в котором попробуем по-своему отобразить некоторые файлы и каталоги:
//CustomFileView.java
//Внешний вид файлов в компоненте JFileChooser import javax.swing.*;
import javax.swing.filechooser.*; import java.awt.*;
public class CustomFileView extends JFrame { public CustomFileView() {
super("CustomFileView"); setDefaultCloseOperation(EXIT_ON_CLOSE);
//выводим окно на экран setSize(300, 200); setVisible(true);
//настраиваем компонент для выбора файлов
JFileChooser chooser = new JFileChooser(); chooser.setDialogTitle("Выберите файл");
7 Подобная ситуация типична для компонентов Swing и их внешнего вида: как правило, внешний вид определяется сторонними объектами. Это снова тот самый шаблон проектирования стратегия (strategy).
428 |
ГЛАВА 14 |
chooser.setFileView(new NewFileView( chooser.getFileSystemView()));
// показываем диалоговое окно
int res = chooser.showOpenDialog(this);
}
// объект, определяющий внешний вид файлов class NewFileView extends FileView {
// значки, применяемые для файлов
private Icon fileIcon = new ImageIcon("file.gif"); private Icon folderIcon = new ImageIcon("folder.gif");
//конструктору необходимо описание файловой системы public NewFileView(FileSystemView fileSystem) {
this.fileSystem = fileSystem;
}
private FileSystemView fileSystem;
//возвращает значок для файла
public Icon getIcon(java.io.File file) {
// основные части файловой системы пропускаем if ( fileSystem.isRoot(file) ||
fileSystem.isDrive(file) ) return null;
if ( file.isDirectory() ) return folderIcon;
else return fileIcon;
}
}
public static void main(String[] args) { SwingUtilities.invokeLater(
new Runnable() {
public void run() { new CustomFileView(); } });
}
}
Мы создаем небольшое окно, после его вывода на экран появится стандартное диалоговое окно для выбора файлов со специальным объектом, задающим внешний вид файлов. Настройка компонента JFileChooser здесь аналогична настройке в уже рассмотренных примерах: для диалогового окна задается заголовок, после чего оно выводится на экран.
Во внутреннем классе NewFileView, унаследованном от абстрактного класса FileView, мы описываем, как должны выглядеть файлы в нашем компоненте JFileChooser. В классе FileView имеется несколько методов, отвечающих в том числе за описание файла и его типа, однако мы ограничиваемся настройкой значков, которые будут соответствовать файлам, переопределяя только один метод getIcon(). В этом методе необходимо вернуть значок Icon. Мы будем возвращать собственные значки для всех файлов и каталогов. Правда, при работе с методами класса FileView надо иметь в виду следующее: в них передаются все отображаемые компонентом JFileChooser элементы, в том числе файлы, представляющие со-
Стандартные диалоговые окна |
429 |
бой корни файловой системы, устройства чтения гибких дисков и компакт-дисков, особые каталоги пользователя и т. п. Если для таких особых файлов возвращать те же значки, что применяются для обычных файлов и каталогов, пользователю станет неудобно работать с компонентом для выбора файлов и выглядеть такой компонент будет не лучшим образом. Именно поэтому мы передаем в конструктор внутреннего класса ссылку на используемое компонентом JFileChooser описание файловой системы FileSystemView. Вызывая методы объекта FileSystemView, мы узнаем, не принадлежит ли файл к «особенным», и для таких «особенных» файлов возвращаем вместо значка пустую (null) ссылку. Это означает, что значок для таких элементов подыщет текущий UI-представитель компонента JFileChooser. Обычные файлы и каталоги будут отображаться с нашими собственными значками.
Запустив программу с примером, вы увидите, как теперь выглядят каталоги и файлы в списке файлов компонента JFileChooser. Как правило, объект, отвечающий за внешний вид файлов, работает в тандеме с фильтром файлов определенного типа, так что пользователь сразу оценивает, какие файлы ему предстоит открывать и быстро запоминает их внешний вид. Как мы уже отметили, в классе FileView имеются и другие методы, которые позволят вам, к примеру, на свой лад описать файл или его тип (по умолчанию описания у файла либо вообще нет, либо используется описание файла, получаемое от операционной системы). Все они легко найдутся в интерактивной документации JDK.
Дополнительные компоненты
Несмотря на то, что компонент для выбора файлов JFileChooser чаще всего используется в своем стандартном виде, вы не ограничены тем, что вам предлагает библиотека, и можете добавить к стандартным компонентам, составляющим JFileChooser, свои собственные компоненты. Дополнительные компоненты, как правило, служат для предварительного просмотра содержимого файлов или для произведения над ними каких-либо действий без выхода из модального диалогового окна, в котором располагается JFileChooser. Специально для поддержки дополнительных компонентов в классе JFileChooser определен метод setAccessory(), который вы можете вызвать для добавления своего компонента (добавить, правда, можно только один компонент, но он может состоять из многих других компонентов). Куда будет добавлен ваш компонент, зависит от используемого внешнего вида, обычно он размещается рядом со списком файлов.
Как правило, при добавлении своих собственных компонентов вас начинает интересовать, что происходит в компоненте JFileChooser, например, какой именно файл в данный момент выбран пользователем. Это необходимо для совершения действий над файлом или его предварительного просмотра, даже если выбор пользователя не окончательный. Специального события, сообщающего о выборе очередного файла из списка, в классе JFileChooser нет, однако не стоит забывать о механизме привязанных свойств JavaBeans. Все наиболее важные свойства компонентов Swing являются привязанными, и узнать о любом их изменении вы сможете, присоединив к компоненту слушателя PropertyChangeListener. Компонент JFileChooser не является исключением: свойства, управляющие выделенным в данный момент файлом или каталогом, также относятся к привязанным, поэтому вы всегда будете знать, к какому файлу или каталогу перешел пользователь, и сможете произвести с этим файлом или каталогом необходимые действия.
А теперь добавим к стандартному компоненту для выбора файлов панель для предварительного просмотра (в нашем примере она будет поддерживать предварительный просмотр изображений). Мы будем следить за сменой выбранного файла (с помощью привязанного свойства) и, если выбранный файл будет хранить изображение, выводить на экран уменьшенный вариант этого изображения. К компоненту JFileChooser мы также присоединим фильтр файлов, который будет выбирать только файлы с изображениями — так просматривать и выбирать изображения будет еще удобнее.
430 |
ГЛАВА 14 |
//PreviewingFiles.java
//Предварительный просмотр файлов
//в компоненте JFileChooser import javax.swing.*;
import javax.swing.filechooser.*; import java.beans.*;
import java.awt.*;
public class PreviewingFiles extends JFrame { public PreviewingFiles() {
super("PreviewingFiles"); setDefaultCloseOperation(EXIT_ON_CLOSE);
//выводим окно на экран setSize(300, 200); setVisible(true);
//настраиваем компонент для выбора файла
JFileChooser chooser = new JFileChooser(); chooser.setDialogTitle("Выберите изображение");
//присоединяем фильтр chooser.setFileFilter(new ImageFilesFilter());
//убираем универсальный фильтр chooser.setAcceptAllFileFilterUsed(false);
//присоединяем дополнительный компонент
Previewer previewer = new Previewer(); chooser.setAccessory(previewer);
//регистрируем в качестве слушателя chooser.addPropertyChangeListener(previewer);
//выводим диалоговое окно на экран
int res = chooser.showOpenDialog(this); if ( res == JFileChooser.APPROVE_OPTION )
JOptionPane.showMessageDialog(
this, chooser.getSelectedFile());
}
// компонент для предварительного просмотра class Previewer extends JPanel
implements PropertyChangeListener { private JLabel label;
public Previewer() {
//настраиваем контейнер setLayout(new BorderLayout());
setPreferredSize(new Dimension(200, 200));
//создаем надпись в панели прокрутки
Стандартные диалоговые окна |
431 |
label = new JLabel();
JScrollPane scroller = new JScrollPane(label); add(scroller);
}
public void propertyChange(PropertyChangeEvent e) { if ( e.getPropertyName().equals(
JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) { // сменился выбранный файл, покажем его
if ( e.getNewValue() != null ) label.setIcon(new ImageIcon(
e.getNewValue().toString()));
}
}
}
// фильтр, отбирающий файлы с изображениями class ImageFilesFilter extends FileFilter {
// принимает файл или отказывает ему public boolean accept(java.io.File file) {
// все каталоги принимаем
if ( file.isDirectory() ) return true;
//имя файла не будет учитывать регистр
String name = file.getName().toLowerCase();
//для файлов смотрим на расширение return ( name.endsWith(".jpg") ||
name.endsWith(".gif") || name.endsWith(".png") );
}
// возвращает описание фильтра public String getDescription() {
return "Изображения (*.jpg, *.gif, *.png)";
}
}
public static void main(String[] args) { SwingUtilities.invokeLater(
new Runnable() {
public void run() { new PreviewingFiles(); } });
}
}
Мы создаем небольшое окно, после его вывода на экран появится модальное диалоговое окно с компонентом для выбора файлов. Настройка компонента для выбора файлов происходит в несколько этапов: для начала мы создаем объект JFileChooser и задаем заголовок для будущего диалогового окна. Затем присоединяется фильтр файлов: он описан во внутреннем классе и будет принимать только файлы, хранящие изображе-
432 |
ГЛАВА 14 |
ния (отбор производится на основе расширения файла). Так как изображения некоторые программы и аппаратные устройства именуют по-разному, имеет смысл проверять окончание файлов вне зависимости от регистра. Для этого мы делаем имя файла строчным. Чтобы пользователь не смог выбрать фильтр без ограничений, мы отключаем его методом setAcceptAllFileFilterUsed(false). Напоследок производится настройка компонента для предварительного просмотра изображений Previewer: после создания мы присоединяем его к компоненту для выбора файлов, применяя метод setAccessory(). Прежде чем вывести компонент JFileChooser на экран, мы регистрируем в нем слушателя событий PropertyChangeEvent, которые запускаются при смене значений привязанных свойств. Для удобства слушатель реализован все в том же классе Previewer.
Компонент, реализующий предварительный просмотр изображений, описан во внутреннем классе Previewer и унаследован от панели JPanel. Состоит он из панели прокрутки, добавленной в центр компонента (в компоненте задействуется менеджер расположения BorderLayout), в панели прокрутки располагается надпись JLabel, которая и будет использоваться для вывода изображения. Заметьте, что мы вручную указываем предпочтительный размер компонента: если этого не сделать, он будет динамически менять свой размер в зависимости от размеров просматриваемого изображения, затрагивая остальной интерфейс, а это сделает работу пользователя некомфортной. Самое интересное происходит в методе propertyChange(), вызываемом при смене значения какого-либо привязанного свойства компонента JFileChooser. Мы следим за свойством, которое хранит имя выделенного в данный момент файла, название этого свойства содержится в константе
SELECTED_FILE_CHANGED_PROPERTY класса JFileChooser. Если свойство с этим названием поменялось, мы проводим следующие действия: проверяем, что новое значение свойства не равно пустой ссылке null (это происходит, когда пользователь обнуляет выделенный файл, например выделяя каталог или полностью сбрасывая выделение), и если это так, создаем новый значок ImageIcon, передавая ему имя нового выделенного файла, и устанавливаем для надписи JLabel полученный значок. Если вы помните, в главе 8 мы говорили, что надписи проводят автоматическую проверку корректности и перерисовку при смене своих свойств, так что вызова метода setIcon() будет достаточно для того, чтобы в нашем компоненте появилось новое изображение.
Стандартные диалоговые окна |
433 |
Запустив пример, вы оцените, насколько удобнее выбирать изображения, когда есть возможность не только увидеть название файла, в котором они хранятся, но и быстро взглянуть на само изображение. Создавая подобным образом любой необходимый вашему приложению компонент, вы сможете присоединить его к компоненту JFileChooser и намного повысить удобство работы с последним.
На самом деле останавливаться на только продемонстрированном подходе вовсе не обязательно. Не забывайте о том, что компонент JFileChooser — самый обычный компонент, пусть и состоящий из множества других компонентов, так что вы вполне можете добавить его в свои диалоговые окна наряду с многими другими компонентами. Функциональность JFileChooser легко дополнить с помощью нескольких дополнительных компонентов. Вы даже сможете убрать из компонента JFileChooser ряд кнопок («ОК» и «Отмена»), вызвав метод setControlButtonsAreShown(false), и заменить их более подходящим для вашей ситуации вариантом. Всего этого достаточно для того, чтобы сделать выбор файлов в компоненте JFileChooser не только простым и быстрым (как для пользователя, так и для программиста), но и удивительно гибким.
Выбор цвета в компоненте JColorChooser
Еще одно стандартное диалоговое окно, любезно предоставляемое нам Swing, реализовано в классе JColorChooser и предназначено для максимально быстрого (для программиста) и удобного (для пользователя) выбора цвета. Работать с компонентом JColorChooser так же просто, как и с компонентом для выбора файлов JFileChooser, а во многом и проще, так как возможностей тонкой настройки у JColorChooser куда меньше. Типичная схема выбора цвета такова: вы создаете объект класса JColorChooser и по мере необходимости выводите его на экран в диалоговом окне стандартного вида с помощью метода showDialog(), попутно задавая родительский компонент и заголовок окна. Рассмотрев пример, мы увидим, каким образом JColorChooser чаще всего применяется в программах:
//SimpleColorChooser.java
//Выбор цвета с помощью JColorChooser import javax.swing.*;
import java.awt.*; import java.awt.event.*;
public class SimpleColorChooser extends JFrame { public SimpleColorChooser() {
super("SimpleColorChooser"); setDefaultCloseOperation(EXIT_ON_CLOSE);
// кнопка выводит на экран окно выбора цвета
JButton choose = new JButton("Выбор цвета фона"); choose.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) { Color color = JColorChooser.showDialog(
SimpleColorChooser.this, "Выберите цвет фона",
getContentPane().getBackground()); // если цвет выбран, используем его
434 |
ГЛАВА 14 |
if ( color != null) getContentPane().setBackground(color);
repaint();
}
}); // выводим окно на экран
setLayout(new FlowLayout()); add(choose);
setSize(300, 200); setVisible(true);
}
public static void main(String[] args) { SwingUtilities.invokeLater(
new Runnable() {
public void run() { new SimpleColorChooser(); } });
}
}
В примере мы создаем небольшое окно, а компонент JColorChooser позволит нам выбрать цвет фона для панели содержимого. В качестве панели содержимого по умолчанию выступает простая панель JPanel, а она по умолчанию непрозрачна, то есть закрашивает все свое пространства цветом фона. Так что для смены цвета фона всего нашего окна нам достаточно будет сменить цвет фона панели содержимого, вызвав метод setBackground().
Объект JColorChooser, так же как и объект JFileChooser, может создаваться в программе единожды, а затем с большими или меньшими вариациями в настройке использоваться на протяжении всего времени ее работы. Это значительно ускоряет процесс появления диалогового окна на экране (если сравнивать с созданием объекта JColorChooser прямо перед выводом диалогового окна на экран) и экономит ресурсы, но требует от вас самостоятельно найти для него место на экране или создать диалоговое окно. С другой стороны, вывести стандартное диалоговое окно для выбора цвета на экран совсем просто: надо всего лишь вызвать статический метод showDialog(), которому передается три параметра. Первым параметром является родительский компонент диалогового окна (это может быть любой компонент, относительно него диалоговое окно располагается на экране), в качестве второго параметра вы задаете заголовок диалогового окна, а третьим параметром является цвет, изначально выбранный в компоненте JColorChooser. Третий параметр можно задать и пустой (null) ссылкой, в таком случае изначально цвета выбрано не будет. В ответ метод showDialog() (он выводит на экран модальное диалоговое окно, так что вам придется подождать, пока оно закончит свою работу) вернет вам цвет Color, который выбрал пользователь, или пустую (null) ссылку, если пользователь передумал и решил цвет не выбирать. В примере мы проверяем, что выбор был сделан, и устанавливаем выбранный цвет в качестве цвета фона.
В компоненте JColorChooser имеется возможность дополнительной настройки: к стандартным способам выбора цвета (из вполне приличного заранее определенного набора, с помощью цветовых моделей от RGB до CMYK) вы можете добавить собственную панель для выбора цвета, унаследовав ее от абстрактного класса AbstractColorChooserPanel из пакета javax.swing.colorchooser. Однако требуется это крайне редко: стандартные способы выбора цвета, представленные нам компонентом JColorChooser, достаточно хороши. Кроме того, вы можете присоединить к компоненту JColorChooser панель просмотра
