12 agosto, 2009

Moviendo un JScrollPane de forma automatica

Uno de los problemas clasicos al tener un JPanel dentro
de un JScrollPane es como mover automaticamente la
barra de Scroll del JScrollPane. Para ello existe el metodo
scrollRectToVisible(Rectangle r) que hara que el o los
ScrollBar se desplazen para hacer que el rectangulo indicado
sea visible.

Hasta aqui la cosa no es muy complicada, pero ¿que ocurre
si dentro de nuestro JPanel tenemos uno o mas JPanel
anidados? La cosa tiende a complicarse un poco ya
que la posicion final de nuestro componente es desconocida
para el JScrollPane.

Consideremos el siguiente ejemplo:


--------------------
JScrollPane scrPane = new JScrollPane();
JPanel panelPrincipal = new JPanel();
JPanel panelAnidado1 = new JPanel();
JPanel panelAnidado2 = new JPanel();

JLabel label = new JLabel("....");
JTextField txt = new JTextField("......")M

panelAnidado2.add(txt);
panelAnidado2.add(label)

panelAnidado1.add(panelAnidado2);
panelPrincipal.add(panelAnidado1)

scrPane.setViewportView(panelPrincipal);
----------------------


Si queremos desplazar el JScrollPane hasta la posicion del
label o el txt, la primera idea que se nos viene a la cabeza
seria:


-------
scrPane.scrollRectToVisible(label.getBounds());
-------


Lamentablemente esto no funcionara ya que scrPane solo
"sabe de la existencia" de panelPrincipal e ignora su contenido.
Por lo tanto lo que debemos hacer es calcular la posicion
relativa de label con respecto a panelPrincipal.

Para ello, el siguiente codigo de ejemplo puede ser util;



-------------

//obtengo el padre del label (panelAnidado2)
JPanel padre = (JPanel) label.getParent();
//Obtengo la posicion del padre
Point posPadre = padre.getLocation();
//Obtengo el 'padre del padre' (panelAnidado1) y su posicion
JPanel padre2 = (JPanel)
Point posPadre2 = padre2.getLocation();

//Conociendo las posiciones, puedo hacer el calculo
Double y = posPadre2.getY() + posPadre1.getY() + label.getLocation.getY();

 /*
  * la variable y contiene la distancia desde el borde del
  * panelAnidado1 hasta el borde del label
  */
//Creo un punto en la posicon relativa del Label

Point relativo = new Point(new Double(posPadre2.getX()).intValue(), y.intValue());

//Con este punto relativo puedo crear un rectangulo y desplazar el JScrollPane

Rectangle moverA = new Rectangle(relativo, label.getPreferredSize());
scrPane.scrollRectToVisible(moverA);

---------------


Resumen:

Como 'panelPrincipal' desconoce la presencia y posicon
de label, calculamos la posicion relativa de label con
respecto a 'panelPrincipal' en base a la posicion de su padre
y cualquier panel previo. Una vez conocida esta posicion
relativa, basta desplazar el scrPane hasta un rectangulo
ubicado en esta posicion relativa con el tamaño del
componente que queremos enfocar.


Agregando este codigo a un FocusListener, es facil
hacer que el JScrollPane se desplace hasta la posicion
del componente que obtiene el foco:

Ejemplo:


--------------


private JScrollPane scroll;
private Jpanel panel;
private FocusListener focoDesplazamiento = new FocusAdapter() {

@Override
public void focusGained(FocusEvent e) {
    Point posicionScroll = scroll.getLocation();
   
    Container parent = e.getComponent().getParent();
    int y = posicionOpcional.y + parent.getLocation().y;
    Point moverA = new Point(posicionScroll.x, y + parent.getHeight());

    panel.scrollRectToVisible(new Rectangle(moverA, parent.getPreferredSize()));
    }
};








....



  
  /*
   * En alguna parte del codigo, hacemos que el JPanel panel este contenido
   * dentro del JScrollPane scroll

   *
   */


   panel = new JPanel();
   scroll = new JScrollPane(panel);


   /*
    * Luego, a todos los componentes que se agregan a panel se les agrega
    * nuestro FocusListener
    */


    JTextField txt1 = new JTextField();
    txt1.addFocusListener(focoDesplazamiento);
    panel.add(txt1);
    
    ...


    //y asi sucesivamente.
}

--------------


bytes!

No hay comentarios.: