viernes, 18 de enero de 2013

Crear un HorizontalScrollView programáticamente en Android

Deseaba crear un HorizontalScrollView similar al que usa Angry Birds al momento de elegir al nivel que queremos jugar, pero de manera programática porque hacerlo a través de XMLs me complicaba un poco el cálculo de los tamaños de los objetos.
 
Fue un dolor de cabeza y por más que buscaba en Internet no encontraba algún artículo que me explicara paso por paso cómo hacerlo, hasta que encontré el siguiente link que me ayudó a entender algunos conceptos: http://www.arpitonline.com/blog/2012/07/01/creating-custom-layouts-for-android/, pero además tuve que ingeniarme algunas cositas extras para hacerlo funcionar tal como quería. Posteo aquí los pasos a seguir:
  1. Crea una clase que herede de ViewGroup. ViewGroup es un View que te permite agregar Views dentro de él. Llamaremos a esta clase MyGroup. Esta clase tendrá dos objetos que nos ayudarán a crear el efecto del Scroll, además de los Views que se agregarán en él:
  2.  
    HorizontalScrollView scroll;
    LinearLayout layout;
     
  3. Dentro del constructor de MyGroup insertamos el siguiente código para agregar el scroll:
  4.  
    scroll = new HorizontalScrollView(context);
    scroll.setHorizontalScrollBarEnabled(false);
    addView(scroll);
    layout = new LinearLayout(context);
    scroll.addView(layout);
     
    HorizontalScrollView es una clase que solamente permite agregarle un hijo, éste será nuestro layout, el cual contendrá todos los views que formarán parte del scroll.
     
  5. Agrega al layout los objetos que se mostrarán en el scroll a través de la sentencia layout.addView(view).
  6.  
  7. Tal como lo describe el artículo, los objetos internos del ViewGroup no se mostrarán a menos que les indiquemos su posición y tamaño, esto lo hacemos sobreescribiendo el método onLayout. En este método, para cada view dentro de nuestro ViewGroup debemos llamar su método layout, algo similar a esto:
  8.  
    for(int i=0;i
        View v = getChildAt(i);
        v.layout(x1,y1,x2,y2);
    }
     
    Esto funciona para views simples, pero en el caso de layouts, éstos también deben indicarle a sus hijos su posición y tamaño ya que hasta ese momento tienen tamaño 0. Una forma común y sugerida en el artículo para resolver esto es sobreescribir el método onMeasure del layout para así poder indicarle a sus hijos el tamaño que tendrán. En mi caso, el único objeto que forma parte del ViewGroup es el scroll, y dentro de éste esta el layout y dentro del layout están los demás views que también deben recibir su tamaño y posición. Así que utilice el onLayout para indicar estos tamaños. Pero probando de varias maneras los objetos no salían como deseaba, hasta que me di cuenta de algo: layout debe tener un ancho igual a la suma de los anchos de sus hijos mas los espacios entre ellos, y el scroll debe tener el ancho en el que deseo aparezcan los views. Aunque puede parecer obvio, puede generar mucha confusión; si voy a poner un objeto seguido de otro sus anchos se calculan diviendo el ancho de la pantalla entre el numero de objetos, pero si quiero poner espacios entre ellos, hay que tomar en cuenta que el layout debe contemplar estos espacios y que es probable que sea incluso más grande que el scroll. El código sería algo así:
     
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        //Quiero que el scroll sea de alto la mitad de la pantalla y que esté centrado
        scroll.layout(0, b/4, r, b*3/4);
     
        //Calculo el ancho que tendrá cada objeto
        int w = (int)(b/2f*tasa);
     
        //El ancho del layout será igual a la suma de los anchos más las separaciones más la posicion inicial
        layout.layout(0, 0, (w+separacion)*(n-1)+w+pos_inicial, b/2);
     
        //Calculo la posición de los views dentro del layout
        for(int i=0; i< layout.getChildCount(); i++) {
            View v = layout.getChildAt(i);
            v.layout((w+separacion)*i+pos_inicial, 0, (w+separacion)*i+w+pos_inicial, b/2);
        }
    }
     
Una vez hecho, el scroll funcionó perfectamente en cualquier dispositivo. Ahora habrá que añadirle el onTouch y los damos eventos.
 
No es la única forma de hacerlo, pero me funcionó muy bien!

No hay comentarios:

Publicar un comentario