## Cómo utilizar la Clase ScrollPane

Un JScrollPane proporciona una vista desplazable de un componente ligero. Cuando el estado de la pantalla real está limitado, se utiliza un ScrollPane para mostrar un componente que es grande o cuyo tamaño puede cambiar dinámicamente.

El código para crear un panel desplazable puede ser mínimo. Por ejemplo aquí tenemos una imagen de un programa que utiliza un panel desplazable para ver una salida de texto:

Y aquí está el código que crea el área de texto, crea el cliente del panel desplazable, y añade el panel desplazable a la ventana:

    textArea = new JTextArea(5, 30);
    JScrollPane scrollPane = new JScrollPane(textArea);
    ...
    contentPane.setPreferredSize(new Dimension(400, 100));
    ...
    contentPane.add(scrollPane, BorderLayout.CENTER);

El programa proporciona el área de texto como argumento al constructor del JScrollPane. Esto establece el área de texto como el cliente del panel desplazable. El panel desplazable maneja todo esto: crear las barras de desplazamiento cuando son necesarias, redibujar el cliente cuando el usuario se mueve sobre él, etc.

Observa que el código de ejemplo selecciona el tamaño preferido del contenedor del panel desplazable. Una alternativa sería seleccionar el tamaño preferido del propio panel desplazable. De cualquier modo, se está limitando el tamaño del panel desplazable. Esto es necesario porque el tamaño preferido de un panel desplazable es ser tan grande como pueda.

Por defecto, un panel desplazable intenta redimensionarse para que su cliente se muestre en su tamaño preferido. Muchos componetes tienen un sencillo tamaño preferido que es lo suficientemente grande como para dibujarse entero. Esto hace que el panel desplazable sea redundante. Otros componentes, como listas, tablas, componentes de texto, y árboles, reportan un tamaño preferido separado para desplazamiento, que normalmente es más pequeño que el tamaño preferido estándard. Por ejemplo, por defecto, el tamaño preferido de una lista para despalzarla es lo suficientemente grande para mostrar ocho filas. Si el tamaño preferido reportado por el componente, no es el que queremos, se selecciona el tamaño preferido del panel desplazable o de su contenedor.

Si todo lo que necesitas es proprocionar desplazamiento básico para un componente ligero, no leas más. Lo que demuestra esta ejemplo es suficiente para la mayoría de los programas.

Sin embargo, un ScrollPane es un objeo altamente personalizable. Se puede determinar bajo que circunstancias se mostrarán las barras de desplazamiento. También se puede decorar con una fila de cabecera, una columna de cabecera y esquinas. Finalmente se puede crear un cliente de desplazamiento que seguro que avisa al panel desplazable sobre el comportamiento de desplazamiento como los incrementos de unidad y de bloques. Estos tópicos se cubren en las siguientes secciones:

- Cómo funciona un Scroll Pane
- Seleccionar el Vigilante de ScrollBar
- Proporcionar decoración personalizada
- Implementar un Cliente de Desplazamiento seguro
- El API de Scroll Pane
- Ejemplos que utilizan JScrollPane

#### Cómo funciona un ScrollPane

> Aquí puedes ver una imagen de una aplicación que utiliza un panel desplazable para ver una foto del padre de Mary cuando era joven:

> El panel desplazable de esta aplicación parece totalmente diferente del de la aplicación anterior. En vez de texto, contiene una gran imagen. El ScrollPane también tiene dos barras de desplazamiento, un fila y una columna de cabecera y tres esquinas personalizadas, una de las cuales contiene un botón.

> > Intenta esto:
> > 1. Compila y ejecuta la aplicación. El fichero fuente es ScrollDemo.java. También necesitarás ScrollablePicture.java, Rule.java, Corner.java y youngdad.jpeg.
> > 2. Mueve las barras de desplazamiento. Mira que la imagen se mueve y con ella las reglas vertical y horizontal.
> > 3. Pulsa el botón cm en la esquina superior izquierda. Las unidades de las cabecera de fila y de columna cambian a pulgadas (o vuelve a centímetros).
> > 4. Pulsa las flechas de las barras de desplazamiento. También, pulsa sobre el camino de las barras de desplazamiento arriba o debajo de la barra vertical, o a la izquierda o derecha de la barra horizontal.
> > 5. Aumenta el tamaño de la ventana. Observa que las barras desaparecen cuando no son necesarias y como resultado las esquina añadidas también desparecen. Disminuye la ventana y las barras de desplazamiento y las esquinas reaparecerán.

> Este programa establece el cliente cuando crea el panel desplazable:

>     // where the member variables are declared
>     private ScrollablePicture picture;
>     ...
>     	// where the GUI is created
>             picture = new ScrollablePicture( ... );
>             JScrollPane pictureScrollPane = new JScrollPane(picture);

> Se puede cambiar dinámicamente el cliente del panel desplazable llamado al método setViewportView.

> Cuando se manipulan las barras de desplazamiento de un ScrollPane, se cambia el área del cliente que es visible. Es imagen muestra esta relación e indica las clases que ayudan al ScrollPane:

> Cuando se mueve la barra de desplazamiento arriba y abajo, el área visible del cliente se mueve arriba y abajo. De forma similar trabaja la barra de desplazamiento horizontal.

> Un ScrollPane utiliza un ejemplar de JViewport para manejar el área visible de un cliente. Este objeto calcula los límites del área visible actual, basándose en las posiciones de las barras de desplazamiento, y la muestra. Un ScrollPane utiliza dos ejemplares separados de JScrollBar para las barras de desplazamiento. Las barras proporcionan el interface para que el usuario manipule el área visible.

> Normalmente los programas no ejemplarizan directamente, ni llaman a los métodos de JViewPort o JScrollBar. En su lugar, los programas alcanzan su comportamiento desplazable utilizando el API de JScrollPane o el API descrito en Implementar un Cliente de Desplazamiento Seguro. Algunos componentes de desplazamiento seguro como JTable y JTree también proporcionan algún API para ayudar a efectuar su desplazamiento.

#### Seleccionar el Vigilante de ScrollBar

> Cuando arranca, la aplicación ScrollDemo contiene dos barras de desplazamiento. Si agrandamos la ventana, las barras de desplazamiento desparecen porque ya no son necesarias. Este comportamiento esta controlado por el Vigilante de SCrollBar. Realmente hay dos vigilantes, uno para cada una de las barras de desplazamiento.

> De los constructores proporcionados por JScrollPane, dos nos permite seleccionar vigilantes de barras de desplazamiento cuando creamos nuestro ScrollPane:

>     JScrollPane(Component, int, int)
>     JScrollPane(int, int)

El primer int especifica el vigilante para la barra vertical, y el segundo para la horizontal. También se pueden seleccionar los vigilantes con los métodos setHorizontalScrollBarPolicy y setVerticalScrollBarPolicy. En ambos casos se utilizan los siguientes enteros definidos en el interface ScrollPaneConstants que es implementado por JScrollPane:

Vigilante | Descripción
--- | ---
VERTICAL_SCROLLBAR_AS_NEEDED HORIZONTAL_SCROLLBAR_AS_NEEDED | Valor por defecto. Las barras de desplazamiento aparecen cuando el JViewPort es más pequeño que el cliente y desaparecen cuando es más grande.
VERTICAL_SCROLLBAR_ALWAYS HORIZONTAL_SCROLLBAR_ALWAYS | Siempre muestra las barras.
VERTICAL_SCROLLBAR_NEVER HORIZONTAL_SCROLLBAR_NEVER | Nunca muestra las barras de desplazamiento. Se utiliza esta opción si no queremos darle al usuario el control sobre la parte del cliente a visualizar. Quizás tengamos alguna aplicación que requiera que el desplazamiento ocurra programáticamente.

#### Proprorcionar Decoración Personalizada

> El área dibujada por un ScrollPane está dividida, al menos, en nueve partes: el centro, cuadro laterales y cuatro esquinas. El centro es el único componente que siempre está presente en un ScrollPane. Cada uno de los cuatro lados son opcionales. El lado superior puede contener una columna de cabecera, el lado izquierdo puede contener una fila de cabecera, el lado inferior puede contener una barra de desplazamiento horizontal, y el lado derecho puede tener una barra de desplazamiento vertical. La presencia de las cuatro esquinas depende completamente de la presencia de los dos laterales que interseccionan en ellas.

> Como se ve en la figura, el ScrollPane de ScrollDemo.java tiene cabeceras de fila y columna personalizadas. Además, como los cuatro laterales están llenos, las cuatro esquinas están presentes. Tres de las esquinas también están personalizadas.

> Las cabeceras de fila y columna del ScrollPane están proporcionadas por una subclase personalizada de JComponent, Rule.java, que dibuja una regla en centímetros o pulgadas. Aquí está el código que crea y selecciona las cabecetas de fila y columna del ScrollPane:

>     ...where the member variables are defined...
>     private Rule columnView;
>     private Rule rowView;
>         ...
>         // Create the row and column headers
>         columnView = new Rule(Rule.HORIZONTAL, false);
>         columnView.setPreferredWidth(david.getIconWidth());
>         rowView = new Rule(Rule.VERTICAL, false);
>         rowView.setPreferredHeight(david.getIconHeight());
>         ...
>         pictureScrollPane.setColumnHeaderView(columnView);
>         pictureScrollPane.setRowHeaderView(rowView);
>         ...

> Se pueden utilizar componentes de peso ligero para cabeceras de fila y columna de un ScrollPane. El ScrollPane pone las cabeceras de fila y columna en su propio JViewPort. Así, cuando se desplaza horizontalmente, la cabecera de columnas la sigue, y cuando se desplaza verticalmente, la cabecera de filas también lo hace.

> Como subclase de JComponent, Rule se dibuja a sí misma sobreescriendo el método paintComponent. Un escurtinio cuidadoso del código revela que se ha tomado un esfuerzo especial para dibujar sólo dentro de los límites actuales. Nuestras cabeceras de fila y columna deben hacer los mismo para asegurarnos un desplazamiento rápido.

> También podemos utilizar cualquier componente de peso ligero para las esquinas de un ScrollPanel. ScrollDemo.java ilustra esto poniendo un botón en la esquina superior izquierda, y objetos Corner personalizados en las esquinas superior derecha e inferior izquiedda. Aquí está el código que crea los objetos Corner y llama a setCorner para situarlos:

>     // Create the corners
>     JPanel buttonCorner = new JPanel();
>     isMetric = new JToggleButton("cm", true);
>     isMetric.setFont(new Font("SansSerif", Font.PLAIN, 11));
>     isMetric.setMargin(new Insets(2,2,2,2));
>     isMetric.addItemListener(new UnitsListener());
>     buttonCorner.add(isMetric); //Use the default FlowLayout

>     ...// Set the corners:
>     pictureScrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER,
>     				buttonCorner);
>     pictureScrollPane.setCorner(JScrollPane.LOWER_LEFT_CORNER,
>     				new Corner());
>     pictureScrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER,
>     				new Corner());

> Recuerda que el tamaño de cada esquina está determinado completamente por el tamaño de los lados que intersecciona allí. Debemos tener cuidado de que el objeto quepa en la esquina. La clase Corner lo hace dibujando dentro de sus límites, que son configurados por el ScrollPane. El botón fue configurado específicamente para caber en la esquina establecida por las cabeceras.

> Como podemos ver desde el código, las constantes indican la posición de las esquinas. Esta figura muestra las constantes para cada posición:

> Las constantes están definidas en el interface ScrollPaneConstants, que es implementado por JScrollPane.

#### Implementar un Cliente de Desplazamiento Seguro

> Para personalizar la forma en la que el componente cliente interactúa con su ScrollPane, podemos hacer que el componente implemente el interface Scrollable. Implementando este interface, un cliente puede especificar tanto al tamaño del cuadro de visión que el ScrollPane utilizará para verlo como la cantidad de desplazamiento para los diferentes controles de las barras de desplazamiento.

> La siguiente figura muestra las tres áreas de un barra de desplazamiento: la barra, los botones y la pista.

> Habrás podido observar cuando manipulabas las barras de desplazamiento de ScrollDemo que pulsando los botones la imagen se desplaza un poco. También podrías haber observado que al pulsar sobre la pista el desplazamiento es mayor. Más generalmente, el botón desplaza el área visible una unidad de incremento y la pista desplaza el área visible un bloque de incremento. El comportamiento que has visto en el ejemplo no es el comportamiento por defecto de un ScroolPane, pero si es especificado por el cliente en su implementación del interface Scrollable.

> El cliente del programa ScrollDemo es ScrollablePicture.java. ScrollablePicture es una subclase de JLabel que proporciona implementaciones para los cinco métodos de Scrollable:

> - getScrollableBlockIncrement
> - getScrollableUnitIncrement
> - getPreferredViewportSize
> - getScrollableTracksViewportHeight
> - getScrollableTracksViewportWidth
> ScrollablePicture implementa el interface Scrollable principalmente para afectar a los incrementos de unidad y de bloque. Sin embargo, debe proporcionar implementaciones para los cinco métodos. Las implementaciones para los tres últimos son razonables por defecto.

> El ScrollPane llama al método getScrollableUnitIncrement del cliente siempre que el usuario pulse uno de los botones de las barras de desplazamiento. Este método devuelve el número de pixels a desplazar. Una implementación obvia de este método devuelve el número de pixels entre marcas de las reglas de cabecera. Pero ScrollablePicture hace algo diferente: devuelve el valor requerido para posiconar la imagen en el límite de una marca. Aquí está la implementación:

>     public int getScrollableUnitIncrement(Rectangle visibleRect,
>     				      int orientation,
>     				      int direction) {
>         //get the current position
>         int currentPosition = 0;
>         if (orientation == SwingConstants.HORIZONTAL)
>             currentPosition = visibleRect.x;
>         else
>             currentPosition = visibleRect.y;

>         //return the number of pixels between currentPosition
>         //and the nearest tick mark in the indicated direction
>         if (direction < 0) {
>             int newPosition = currentPosition -
>     			 (currentPosition / maxUnitIncrement) *
>     			  maxUnitIncrement;
>             return (newPosition == 0) ? maxUnitIncrement : newPosition;
>         } else {
>             return ((currentPosition / maxUnitIncrement) + 1) *
>     		 maxUnitIncrement - currentPosition;
>         }
>     }

> Si la imagen ya se encuentra en un marca, este método devuelve el número de pixels entre marcas. De otro modo devuelve el número de pixels entre la posición actual y la marca más cercana.

> De igual modo el ScrollPane llama al método getScrollableBlockIncrement del cliente cada vez que el usuario pulsa sobre la pista de la barra de desplazamiento. Aquí está la implementación que ScrollablePicture hace de este método:

>     public int getScrollableBlockIncrement(Rectangle visibleRect,
>     				       int orientation,
>     				       int direction) {
>         if (orientation == SwingConstants.HORIZONTAL)
>             return visibleRect.width - maxUnitIncrement;
>         else
>             return visibleRect.height - maxUnitIncrement;
>     }

> Este método devuelve la altura del rectángulo visible menos una marca. Este comportamiento es típico. Un incremento de bloque debería permitir que el JViewpor deje un poco del área visible anterior por razones de contexto. Por ejemplo, un área de texto podría dejar una o dos líneas y una tabla podría dejar una fila o una columna (dependiendo de la dirección de desplazamiento).

> Puedes ver la tabla Implementar el Interface Scrollable para obtener más detalles sobre los métodos definidos en Scrollable.

> El paquete Swing proporciona estas clases de desplazamiento seguro:

> - listas
> - tablas
> - componentes de texto
> - árboles

#### El API de ScrollPane

> Las siguiente tablas listan los métodos y constructores más utilizados de JScrollPane. Otros métodos útiles son definidos por las clases JComponent y Component.

> El API para utilizar ScroolPane se divide en estas categorías:

> - Configurar el ScrollPane
> - Decorar el ScrollPane
> - Implementar el Interface Scrollable

> Configurar el ScrollPane
> Método | Propósito
> --- | ---
> JScrollPane() JScrollPane(Component) JScrollPane(int, int) JScrollPane(Component, int, int) | Crea un ScrollPanel El parámetro Component, cuando existe, selecciona el cliente. Los dos parámetros int, cuando existen, seleccionan los vigilantes de seguridad de las barras de desplazamiento vertical y horizontal (respectivamente):
> void setViewportView(Component) | Selecciona el cliente del ScrollPane.
> void setVerticalScrollBarPolicy(int) int getVerticalScrollBarPolicy() | Selecciona u obtiene el vigilante de desplazamiento vertical. ScrollPaneConstants define tres valores para especificar estos vigilantes: VERTICAL_SCROLL_BAR_AS_NEEDED (por defecto), VERTICAL_SCROLL_BAR_ALWAYS, y VERTICAL_SCROLL_BAR_NEVER.
> void setHorizontalScrollBarPolicy(int) int getHorizontalScrollBarPolicy() | Selecciona u obtiene el vigilante de desplazamiento horizontal. ScrollPaneConstants define tres valores para especificar estos vigilantes: HORIZONTAL_SCROLL_BAR_AS_NEEDED (por defecto), HORIZONTAL_SCROLL_BAR_ALWAYS, y HORIZONTAL_SCROLL_BAR_NEVER.
> void setViewportBorder(Border) Border getViewportBorder() | Selecciona u obtiene el borde alrededor del JViewport.

> Decorar el ScrollPane
> Método | Propósito
> --- | ---
> void setColumnHeaderView(Component) void setRowHeaderView(Component) | Selecciona la cabecera de fila o de columna para el ScrollPane.
> void setCorner(Component, int) Component getCorner(int) | Seleciona u obtiene la esquina especificada. El parámetro int indica qué columna y debe ser una de las siguientes constantes definidas en ScrollPaneConstants: UPPER_LEFT_CORNER, UPPER_LEFT_CORNER, LOWER_LEFT_CORNER, y LOWER_LEFT_CORNER.

> Implementar el Interface Scrollable
> Método | Propósito
> --- | ---
> int getScrollableUnitIncrement(Rectangle, int, int) void getScrollableBlockIncrement(Rectangle, int, int) | Obtiene el incremento de unidad o de bloque en pixels. El parámetro Rectangle son los límites del área visible actualmente. El primer parámetro int es SwingConstants.HORIZONTAL o SwingConstants.VERTICAL dependiendo de la barra que haya pulsado el usuario. El segundo parámetro int indica la dirección del desplazamiento. Un valor menor que 0 indica arriba o izquierda. Una valor mayor que 0 indica abajo o derecha.
> Dimension getPreferredScrollableViewportSize() | Obtiene el tamaño preferido del JViewport. Esto permite al cliente influenciar en el tamaño del componente en el que va ser mostrado. Si este tamaño no es importante devuelve getPreferredSize.
> boolean getScrollableTracksViewportWidth() boolean getScrollableTracksViewportHeight() | Obtiene su el ScrollPane debería forzar al cliente a tener la misma anchura o altura que el JViewport. Devolver true por alguno de esto métodos efectivamente desactiva el desplazamiento horizontal o vertival (respectivamente).

#### Ejemplos que usan JScrollPane

> Esta tabla lista ejemplos que usan JScrollPane y dónde poder encontrarlos.

> Ejemplo | Dónde se describe | Notas
> --- | --- | ---
> ToolBarDemo.java | Esta página y Cómo usar ToolBars. | Muestra un sencillo, pero típico, uso de ScrollPane.
> ScrollDemo.java | Esta página. | Utiliza muchos ScrollPane para campanas y cartas.
> SplitPaneDemo.java | Cómo usar SplitPane y Cómo usar Listas. | Pone una lista en un ScrollPane. También muestra como manejar el caso cuando un cliente desplazable cambia su tamaño.
> TableDemo.java | Cómo usar Tablas. | Pone una Tabla en un ScrollPane.
> TreeDemo.java | Cómo usar Trees. | Pone un árbol en un ScrollPane.

Ozito