## Cómo usar la Clase FileChooser

La clase JFileChooser proporciona un UI para elegir un fichero de una lista. Un selector de ficheros es un componente que podemos situar en cualquier lugar del GUI de nuestro programa. Sin embargo, normalmente los programas los muestran en diálogos modales porque las operaciones con ficheros son sensibles a los cambios dentro del programa. La clase JFileChooser hace sencillo traer un diálogo modal que contiene un selector de ficheros.

Los selectores de ficheros se utilizan comunmente para dos propósitos:

- Para presentar una lista de ficheros que pueden ser abiertos por la aplicación.
- Para permitir que el usuario seleccione o introduzca el nombre de un fichero a grabar.

Observa que el selector de ficheros ni abre ni graba ficheros. Presenta un GUI para elegir un fichero de una lista. El programa es responsable de hacer algo con el fichero, como abrirlo o grabarlo.

Como la mayoría de los programadores sólo quieren un selector para abrir o para grabar ficheros, la clase JFileChooser proporciona los métodos convenientes para mostrar estos tipos de selectores de ficheros en un diálogo. Nuestro primer ejemplo, FileChooserDemo.java, ilustra estos usos:

Cuando se pulsa el botón Open el programa trae un open file chooser. Cuando se pulsa el botón Save el programa trae un save file chooser. Aquí podemos ver una imagen de un selector de apertura de ficheros:

Aquí podemos ver el código que crea y muestra el selector de apertura de ficheros:

    private JFileChooser filechooser = new JFileChooser();
    ...
    int returnVal = filechooser.showOpenDialog(FileChooserDemo.this);

Por defecto, un selector de ficheros que no haya sido mostrado anteriormente muestra todos los ficheros en el directorio del usuario. Podemos especificarle un directorio inicial utilizando uno de los otros constructores de JFileChooser, o podemos selecccionar el directorio directamente con el método setCurrentDirectory.

El programa de ejemplo utiliza el mismo ejemplar de JFileChooser para mostrar el selector de grabar ficheros. Aquí tenemos el método actionPerformed para el oyente del botón Save:

    private JFileChooser filechooser = new JFileChooser();
    ...
    public void actionPerformed(ActionEvent e) {
        int returnVal = filechooser.showSaveDialog(FileChooserDemo.this);

        if (returnVal == JFileChooser.APPROVE_OPTION) {
    	File file = filechooser.getSelectedFile();
    	log.append("Saving: " + file.getName() + "." + newline);
        } else {
    	log.append("Save command cancelled by user." + newline);
        }
    }

Utilizando el mismo selector de ficheros para abrir y grabar ficheros, el programa consigue estos beneficios:

- El selector recuerda el directorio actual entre usos, por eso los diálogos de abrir y grabar comparten el mismo directorio actual.
- Sólo tenemos que personalizar un selector de ficheros, y nuestra personalización se aplicará a las dos versiones, la de apertura y la de grabación.

Cómo hemos podido ver en los fragmentos de código anteriores, los métodos showXxxxDialog devuelven un entero que indica si el usuario ha seleccionado un fichero. Podemos utilizar el valor de retorno para determinar si realizar o no la operación requerida.

Si el usuario elige un fichero, el código llama a getSelectedFile sobre el selector de ficheros para obtener un ejemplar de File, que representa el fichero elegido. El ejemplo obtiene el nombre del fichero y lo utiliza en un mensaje. Podemos utilizar otros métodos del objeto File, como getPath o isDirectory, para obtener información sobre él. También podemos llamar a otros métodos como delete y rename para cambiar el fichero de alguna forma. Por supuesto, podríamos leer o grabar el fichero utilizando una de las clases lectoras o escritoras proporcionadas por el JDK.

Si quieres crear un selector de ficheros para una tarea distinta de abrir o grabar, o si quieres personalizar el selector de ficheros, sigue leyendo. Estos excitantes tópicos se describen más abajo:

- FileChooserDemo: Toma 2
- Usar un FileChooser para una tarea personalizada
- Filtrar la lista de ficheros
- Personalizar la vista de ficheros
- Proporcionar un accesorio de visionado
- El API FileChooser
- Ejemplos que utilizan FileChooser

#### FileChooserDemo: Toma 2

> Echemos un vistazo a FileChooserDemo2.java, una versión modificada del ejemplo anterior que utiliza más el API JFileChooser. Este ejemplo utiliza un selector de ficheros que ha sido personalizado de varias formas. Al igual que el ejemplo original, el usuario llama al selector de ficheros pulsando un botón. Aquí podmemos ver una imagen del selector de ficheros: Necesitaremos estos ficheros fuente para ejecutar el ejemplo : FileChooserDemo2.java, ImageFilter.java, ImageFileView.java, y ImagePreview.java.

> Cono se ve en la figura, este selector de ficheros ha sido personalizado para una tarea especial (enviar), proporciona un filtro de ficheros seleccionable, utiliza un visor especial de ficheros para ficheros de imágenes, y tiene un accesorio de visualización que muestra una versión reducida del fichero de imagen seleccionado.

> El resto de esta página muestra el código que crea y personaliza este selector de ficheros.

#### Usar un Selector de Ficheros para una Tarea Personalizada

> Como hemos visto, JFileChooser proporciona un método para mostrar un selector de apartura de ficheros y otro método para mostrar un selector para grabar ficheros. En el aspecto y comportamiento Metal, la única diferencia entre estos dos selectores es el título de la ventana del díalogo y la etiqueta del botón "accept".

> La clase tiene un tercer método , showDialog, para mostrar un selector de ficheros para una tarea personalizada. Aquí podemos ver el código de FileChooserDemo2 que muestra el diálogo selector de ficheros para la tarea Send.

>     JFileChooser filechooser = new JFileChooser();

>     int returnVal = filechooser.showDialog(FileChooserDemo2.this,
>     				       "Send");

> El primer argumento del método showDialog es el componente padre para el diálogo. El segundo argumento es un String que proporciona tanto el título de la ventana de diálogo como la etiqueta del botón"accept".

> De nuevo, el selector de ficheros no hace nada con el fichero seleccionado. El programa es responsable de implementar la tarea personalizada para la que se creó el selector de ficheros.

#### Filtrar la lista de ficheros

> Por defecto, un selector de ficheros muestra todos los ficheros y directorios que detecta. Un programa puede aplicar uno o más filtros de ficheros a un selector de ficheros para que el selector sólo muestre algunos de ellos. El selector de ficheros llama al método accept del filtro con cada fichero para determinar si debería ser mostrado. Un filtro de ficheros acepta o rechaza un fichero basándose en algún criterio como el tipo, el tamaño, el propietario, etc.

> JFileChooser soporta tres clases de filtrado. Los filtros se chequean en el orden aquí listado. Por eso un filtro del segundo tipo solo puede filtrar aquellos ficheros aceptados por el primero, etc.

> Friltrado interno
>   El filtrado se configura a través de llamadas a métodos específicos de un selector de ficheros. Actualmente el único filtro interno disponible es para los ficheros ocultos. Se llama a setFileHidingEnabled(true) para desactivar la selección de ficheros ocultos (como aquellos que empiezan con '.' en sistemas UNIX).
> Friltrado controlado por la aplicación
>   La aplicación determina los ficheros a mostrar. Se crea una subclase de FileFilter, se ejemplariza, y se utiliza el ejemplar como un argumento para setFileFilter. El selector de fiheros sólo mostrará los ficheros que acepte el filtro.
> Filtrado seleccionable por el usuario
>   El GUI selector de ficheros proporciona una lista de filtros de la que el usuario puede elegir uno. Cuando el usuario elige un filtro, el selector de ficheros muestra sólo aquellos ficheros que acepte el filtro. FileChooserDemo2 añade un filtro de ficheros personalizado a su lista de filtros seleccionables del selector de ficheros:

>       filechooser.addChoosableFileFilter(new ImageFilter());

>   El filtro personalizado se implementa en ImageFilter.java, como una sublcase de FileFilter. La clase ImageFilter implementa el método getDescription para devolver un string y ponerlo en la lista de filtros seleccionables. Como muestra el siguiente código, ImageFilter implementa el método accept para aceptar todos los directorios y cualquier fichero que tenga las extensiones ".jpg", ".jpeg", ".gif", ".tif", or ".tiff".

>       public boolean accept(File f) {

>           if (f.isDirectory()) {
>       	return true;
>           }

>           String s = f.getName();
>           int i = s.lastIndexOf('.');

>           if (i > 0 &&  i < s.length() - 1) {
>       	String extension = s.substring(i+1).toLowerCase();
>       	if (tiff.equals(extension) ||
>       	    tif.equals(extension) ||
>       	    gif.equals(extension) ||
>       	    jpeg.equals(extension) ||
>       	    jpg.equals(extension)) {
>       	        return true;
>       	} else {
>       	    return false;
>       	}
>           }

>           return false;
>       }

>   Aceptando todos los directorios, este filtro permite al usuario navegar a través del sistema de ficheros. Si se omitieran las líneas en negrita de este método, el usuario se vería limitado al directorio en que se inicializó el selector de ficheros.

#### Personalizar un Visor de Ficheros

> Un selector de ficheros presenta una lista de ficheros para elegir uno. En el aspecto y comportamiento Metal, la lista del selector muestra cada nombre de fichero y mustra un pequeño icono que representa si el fichero es un verdadero fichero o un directorio. Podemos personalizar la visión de la lista creando una subclase personalizada de FileView, y utilizando un ejemplar de la clase como un argumento al método setFileView. El ejemplo utiliza un ejemplar de ImageFileView como el visor de ficheros para el selector:

>     filechooser.setFileView(new ImageFileView());

> ImageFileView muestra un icono diferente para cada tipo de imagen aceptada por el filtro de imágenes descrito anteriormente.

> La clase ImageFileView sobreescribe los cinco métodos abstractos definidos en FileView:

> String getTypeDescription(File f)
>   Devuelve una descripción del tipo de fichero. Aquí podemos ver la implementación e este método en ImageFileView:

>       public String getTypeDescription(File f) {
>           String extension = getExtension(f);
>           String type = null;

>           if (extension != null) {
>               if (extension.equals("jpeg")) {
>                   type = "JPEG Image";
>               } else if (extension.equals("gif")){
>                   type = "GIF Image";
>               } else if (extension.equals("tiff")) {
>                   type = "TIFF Image";
>               }
>           }
>           return type;
>       }

> Icon getIcon(File f)
>   Devuelve un icono que representa el fichero o su tipo. Aquí tenemos la implementación de este método en ImageFileView:

>       public Icon getIcon(File f) {
>           String extension = getExtension(f);
>           Icon icon = null;
>           if (extension != null) {
>               if (extension.equals("jpeg")) {
>                   icon = jpgIcon;
>               } else if (extension.equals("gif")) {
>                   icon = gifIcon;
>               } else if (extension.equals("tiff")) {
>                   icon = tiffIcon;
>               }
>           }
>           return icon;
>       }

> String getName(File f)
>   Devuelve el nombre del fichero. La mayoría de las implementaciónes de este método deberían responder null para indicar que el aspecto y comportamiento debería imaginárselo. Otra implementación común devuelve f.getName().

> String getDescription(File f)
>   Devuelve una descripción del fichero. Una implementación común de este método devuelve null para indicar que el aspecto y comportamiento debería imaginárselo.

> Boolean isTraversable(File f)
>   Devuelve si un directorio es atravesable o no. La mayoría de las implementaciones de este método deberían responder null para indicar que el aspecto y comportamiento debería imaginárselo. Algunas aplicaciones podrían querer avisar al usuario de no descender a ciertos tipos de directorios porque representan documentos compuestos.

> La implementación que hace ImageFileView de los métodos getTypeDescription y getIcon utilizan un método personalizado getExtension:

>     private String getExtension(File f) {

>         String ext = null;
>         String s = f.getName();
>         int i = s.lastIndexOf('.');

>         if (i > 0 &&  i < s.length() - 1) {
>             ext = s.substring(i+1).toLowerCase();
>         }
>         return ext;
>     }

#### Proporcionar un acesorio de visionado

> El selector de ficheros personalizado en FileChooserDemo2 tiene un accesorio de visionado. Si el ítem seleccionado es una imagen JPEG, TIFF, o GIF, el accesorio de visionado muestra una pequeña imagen del fichero. Si no lo es, el accesorio de visionado está vacío.

> El ejemplo llama al método setAccessory para establecer un ejemplar de ImagePreview como accesorio de visionado del selector:

>     filechooser.setAccessory(new ImagePreview(filechooser));

> Cualquier objeto que desciende de JComponent puede ser un accesorio de visionado. El componente debería implementar paint o paintComponent, y tener un tamaño predeterminado que parezca adecuado en el selector de ficheros.

> El selector de ficheros dispara un evento de cambio de propiedad cuando el usuario selecciona un ítem de la lista. Por eso, un programa con accesorio de visionado debe registrarse para recibir estos eventos y actualizarse cada vez que la selección cambie. En el ejemplo, el propio objeto ImagePreview se registra para estos eventos. Esto mantiene en una sola clase todo el código relacionado con el accesorio de visionado.

> Aquí podemos ver la implementación del método propertyChange en el ejemplo, que es el método llamado cuando se dispara un evento de cambio de propiedad:

>     public void propertyChange(PropertyChangeEvent e) {
>         String prop = e.getPropertyName();
>         if (prop == JFileChooser.SELECTED_FILE_CHANGED_PROPERTY) {
>             f = (File) e.getNewValue();
>             if (isShowing()) {
>                 loadImage();
>                 repaint();
>             }
>         }
>     }

> Este método carga la imagen y redibuja el accesorio de visionado, si SELECTED_FILE_CHANGED_PROPERTY es la propiedad que ha cambiado.

#### El API de FileChooser

> El API para usar selectores de ficheros se divide en estas gategorías:
> - Crear y Mostrar un Selector de Ficheros
> - Navegar por la Lista del Selector de Ficheros
> - Personalizar un Selector de Ficheros
> - Seleccionar Ficheros y Directorios

> Crear y Mostrar un Selector de Ficheros
> Método | Propósito
> --- | ---
> JFileChooser() JFileChooser(File, FileSystemView) JFileChooser(File) JFileChooser(FileSystemView) JFileChooser(String, FileSystemView) JFileChooser(String) | Crea un ejemplar de JFileChooser.
> int showOpenDialog(Component) int showSaveDialog(Component) int showDialog(Component, String) | Muestra un diálogo modal conteniendo el selector de ficheros.

> Navegar por la Lista del Selector de Ficheros
> Método | Propósito
> --- | ---
> void ensureFileIsVisible(File) | Fuerza el fichero indicado a ser visible en la lista de ficheros.
> void setCurrentDirectory(File) File getCurrentDirectory | Selecciona u obtiene el directorio cuyos ficheros se están mostrando en el selector de ficheros.
> void changeToParentDirectory() | Cambia la lista para mostrar el directorio padre del directorio actual.
> void rescanCurrentDirectory() | Comprueba el sistema de ficheros y actualiza la lista del selector.

> Personalizar el Selector de Ficheros
> Método | Propósito
> --- | ---
> JComponent getAccessory() void setAccessory(JComponent) | Selecciona u obtiene el accesorio del selector de ficheros.
> void setFileFilter(FileFilter) FileFilter getFileFilter() | Selecciona u obtiene el filtro primario del selector de ficheros.
> void setFileView(FileView) FileView getFileView() | Selecciona u obtiene el visor de ficheros del selector.
> FileFilter[] getChoosableFileFilters() void setChoosableFileFilters(FileFilter[]) void addChoosableFileFilter(FileFilter) boolean removeChoosableFileFilter(FileFilter) void resetChoosable(FileFilter) FileFilter getAcceptAllFileFilter() | Selecciona, obtiene o modifica la lista de filtros seleccionables.
> void setFileHidingEnabled(boolean) boolean isFileHidingEnabled() | Selecciona u obtiene si se muestran los ficheros ocultos.

> Seleccionar Ficheros y Directorios
> Método | Propósito
> --- | ---
> void setFileSelectionMode(int) int getFileSelectionMode() boolean isDirectorySelectionEnabled() boolean isFileSelectionEnabled() | Selecciona el modo de selección de ficheros. Los valores aceptables son FILES_ONLY, DIRECTORIES_ONLY, y FILES_AND_DIRECTORIES.
> void setMultiSelectionEnabled(boolean) boolean isMultiSelectionEnabled() | Selecciona u obtiene si se pueden seleccionar varios ficheros a la vez.
> void setSelectedFile(File) File getSelectedFile() | Selecciona u obtiene el fichero actualmente seleccionado.
> void setSelectedFiles(File[]) File[] getSelectedFiles() | Selecciona u obtiene los ficheros actualmente seleccionados.

#### Ejemplos que utilizan FileChooser

> Esta tabla muestra los ejemplos que utilizan JFileChooser y dónde poder encontrarlos.

> Ejemplo | Donde se Describe | Notas
> --- | --- | ---
> FileChooserDemo.java | Esta página | Llama a un selector de apertura de ficheros y a un selector para grabar ficheros.
> FileChooserDemo2.java | Esta página | Utiliza un selector de ficheros con filtrado personalizado, un visor de ficheros personaliado y un accesorio de visionado.

Ozito