jueves, 29 de diciembre de 2011

Resources

Haciendo una gran separación de lo que nos podemos encontrar en un proyecto de Android, podemos decir que tenemos dos cosas; el código JAVA y los recursos.

El código JAVA todos (o casi todos) sabemos lo que es y lo que hace...pero y los recursos?. Pues los recursos no son más que ficheros en XML (la gran mayoría) y algún que otro binario (para imágenes, audio, etc). Estos ficheros serán los encargados de contener la información para pintar nuestra interfaz, cargar una lista desplegable con valores predeterminados, mostrar cadenas de texto, etc.

En Android, todo tiene su sitio y como tal, debemos establecer cada recurso en su lugar correspondiente (podemos inventarnos los nombres de los directorios, pero si lo hicieramos, no estaríamos siguiendo las Best-Practices de Android y sería un verdadero caos a la hora de trabajar en equipo). La gran mayoría de los recursos están contenidos en el directorio RES de nuestra aplicación, a excepción de los RAW que se alojan en un directorio aparte con ese mismo nombre. Para acceder a dichos recursos tenemos la famosa clase R, la cual indexa y da acceso a todos los recursos definidos. Vamos a ver qué subdirectorios y ficheros podemos tener y qué podemos almacenar en cada uno de ellos:
  • VALUES
    • strings.xml: almacena texto estático definido en formato XML como pares de clave-valor.
    • <?xml version="1.0" encoding="utf-8"?>
      <Resources>
       <!-- Pre-defined Strings -->
          <string name="hello">Hello World!</string>
          <string name="app_name">Resources</string>
          <!-- Own Strings -->
          <string name="txtAceptar">Aceptar</string>
      </Resources>
      El atributo "name" identifica cada entrada y será el nombre por el que nos referiremos a ella cada vez que queramos hacer uso de su valor.  
      Accediendo a su valor
      -XML: por ejemplo para establecer el texto de un control en la definición de un layout con el formato "@string/[Name]":
      <TextView 
       android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="@string/txtAceptar"/>
      
      -JAVA: usamos el método getString([String-Name]) 
      String str = this.getString(R.string.txtAceptar);
       
      • arrays.xml: como su propio nombre indica, almacena arrays definidos en formato XML.
      • <?xml version="1.0" encoding="utf-8"?>
         <resources>
             <array name="miarray">
                 <item>Item1</item>
                 <item>Item2</item>
             </array>
         </resources>
        El atributo "item" identifica cada entrada.  
        Accediendo a su valor
        -XML: por ejemplo para establecer el contenido de una lista desplegable (Spinner) a traves de la propiedad "entries" con el formato "@array/[Name]":
        <Spinner
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"
             android:entries="@array/miarray"/>
        
        -JAVA: para acceder a la matriz, necesitamos un "ContextWrapper" (getResources), otorgándonos acceso a todos los recursos.
        //Arrays de cadena
        String[] str_arr = this.getResources().getStringArray(R.array.miarray);
        //Arrays de enteros
        int[] int_arr = this.getResources().getIntArray(R.array.my_int_array);

      • colors.xml: almacenaremos los distintos colores que vayamos a usar en la aplicación. Se define en formato XML.
      • <?xml version="1.0" encoding="utf-8"?>
         <resources>
             <!-- definición hexadecimal como HTML: -->
             <color name="error">#FF0000</color>
             <!-- definición hexadecimal (#AARRGGBB) definiendo el canal alfa -->
             <color name="fondo">#F0053781</color>
         </resources>
        El atributo "name" identifica cada entrada y será el nombre por el que nos referiremos a ella cada vez que queramos hacer uso de su valor.  
        Accediendo a su valor
        -XML: por ejemplo para establecer el color de fondo y del texto de un control en la definición de un layout con el formato "@color/[Name]":
        <TextView 
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"
             android:text="@string/hello"
             android:background="@color/fondo"
             android:textColor="@color/error"/>
        
        -JAVA: a traves del ContextWrapper.
        TextView tex = (TextView) this.findViewById(R.id.text);
        tex.setTextColor( this.getResources().getColor(R.color.error) );

        • dimens.xml: almacena dimensiones predefinidas en formato XML. Las unidades pueden ser in, mm y pt (pulgadas, milímetros y puntos respectivamente basados en la pantalla) y dp y sp (píxeles independientes del dispositivo y de la escala) que permiten un trabajo más facil con la variada gama de tamaños de los diversos dispositivos.
        • <?xml version="1.0" encoding="utf-8"?>
           <resources>
               <dimen name="dimension_milimetros">12mm</dimen>
               <dimen name="dimension_pulgadas">12in</dimen>
           </resources>
          El atributo "name" identifica cada entrada y será el nombre por el que nos referiremos a ella cada vez que queramos hacer uso de su valor.  
          Accediendo a su valor
          -XML: por ejemplo para establecer el tamaño de un control en la definición de un layout con el formato "@dimen/[Name]":
          <TextView 
               android:layout_width="@dimen/dimension_milimetros"
               android:layout_height="@dimen/dimension_pulgadas"
           />
          
          -JAVA: a traves del ContextWrapper.
          float dim = this.getResources().getDimension(R.dimen.dimension_pulgadas);
             
          • styles.xml: almacena estilos y temas en formato XML. Debido a la extensión de los mismos, los estilos y temas los trataremos en un post a parte.

        • DRAWABLE: aquí se almacenaran los iconos e imagenes de nuestra aplicación. Dependiendo de la resolución de la pantalla de nuestro terminal, podemos diferenciar tres subcarpetas:
          • drawable-hdpi: alta definición, formato WVGA(480 X 800 DPI).
          • drawable-mdpi: media definición, formato HVGA(320 X 480 DPI).
          • drawable-ldpi: baja definición, formato QVGA(240 X 320 DPI).
          Accediendo a su valor
          -XML: sólo hay que especificar el nombre de la imagen con el formato "@drawable/[NombreArchivo]":
          <ImageView
               android:layout_width="fill_parent"
               android:layout_height="wrap_content"
               android:src="@drawable/imagen_policia"
            />
          
          -JAVA: a traves del ContextWrapper con el formato "R.drawable/[Name]".
          ImageView view = (ImageView) this.findViewById(R.id.drawme);
          view.setImageResource(R.drawable.imagen_policia);

        • RAW: como comentamos antes, este directorio se aloja fuera del RES. Aquí se almacenaran archivos binarios de nuestra aplicación tales como videos, audios, páginas html, etc.
          Accediendo a su valor
          -JAVA: a traves del ContextWrapper con el formato "R.raw/[Name]".
          //Abrimos el fichero y vemos si tenemos algo
          InputStream ins = getResources().openRawResource(R.raw.config_db);
          int size = ins.available();
          //Leemos el contenido a un array de bytes.
          byte[] buffer = new byte[size];
          ins.read(buffer);
          ins.close();
          //Guardamos la copia del fichero
          FileOutputStream fos = new FileOutputStream("copia_config_db.db");
          fos.write(buffer);
          fos.close();
          

        • LAYOUT: son archivos XML generadores de las pantallas de nuestra aplicación. Esto ya ha quedado explicado en anteriores post.

        • MENU: aquí se almacenarán archivos XML para formar los menús de nuestra aplicación. Debido a la extensión de los mismos, estos menús los trataremos en un post a parte.

        • XML: en esta carpeta almacenaremos en formato XML los ficheros de configuración de nuestra aplicación. Debido a que son ficheros generados por nosotros, pueden tener la apariencia y estructura que nosotros queramos.
        • <?xml version="1.0" encoding="UTF-8"?>
           <root>
               <item>
                   <titulo>Google</title>
                   <descripcion>Buscador web.</abstract>
                   <link standalone="true">http://www.google.es</link>
               </item>
           </root>
          
          Para analizar el XML, usamos la clase "XmlResourceParser" que la veremos en otro post.

        Una muestra de nuestro proyecto con los recursos la tenemos a continuación en la siguiente captura:















        Maikel.

        martes, 27 de diciembre de 2011

        Layouts

        Los layouts son contenedores donde podremos almacenar nuestros controles. Entre las propiedades más utiles de este control podemos encontrar:
        • android:layout_width: indica la anchura del contenedor. Como constantes para esta propiedad tenemos:
          • fill_parent: la anchura del contenedor la establece el layout donde se encuentra contenido (si sólo tenemos un layout, la dimensión del control ocupará toda la pantalla a lo ancho).
          • match_parent: igual al anterior, el tamaño a lo ancho lo especifica el contenedor padre.
          • wrap_content: el ancho del contenedor será el mismo que tenga el control o controles que albergue.
          • variable: este no es una constante (como su nombre indica), sino que nosotros mismos le indicamos qué tamaño queremos para el contenedor (indicándolo mediante px, dp, etc.).
        • android:layout_height: igual que la anterior pero para la altura.
        • android:orientation: depende del tipo de layout, especificaremos orientación Horizontal o Vertical para posicionar los controles.
        • android:id: identificador para referirnos al layout. A través de este ID, accederemos a todas sus propiedades desde JAVA.
        • android:background: color de fondo del layout. El color se especifica en formato RGB hexadecimal con la opción de poder añadir un canal más para el Alpha. A grandes rasgos, este canal Alpha nos indica el nivel de transparencia que tendrá nuestro color. Los formatos para esta propiedad son:
          • #RGB
          • #ARGB
          • #RRGGBB
          • #AARRGGBB

        Los tipos de Layout disponibles son:
        • LinearLayout: cuando creamos una actividad nueva, por defecto trae este tipo de layout. Este contenedor nos agrupa los controles uno detrás de otro alineados a la izquierda. Dependiendo lo que le especifiquemos en su propiedad "orientation", nos agregará los controles de forma horizontal o vertical. En el siguiente código vemos un LinearLayout padre donde especificamos que el ancho sea el de la pantalla, su altura sea de 200dp, la orientación de los controles verticales y su color de fondo azul con el canal alfa transparente. Contiene otro LinearLayout donde especificamos que la anchura sea la que tenga el contenedor padre, la altura se adapte al control que contenga, la orientación de los controles verticales y el color de fondo rojo con una transparencia del 50%. Debido al canal alfa, el color es una mezcla entre el rojo especificado en este layout y del azul del padre, dando un tono morado.
        <?xml version="1.0" encoding="utf-8"?>
        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="200dp"
            android:orientation="vertical" 
            android:background="#ff0000ff"
            android:id="@+id/lyEjemplo_layouts">
         
            <LinearLayout
                android:id="@+id/linearLayout1"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                android:background="#88ff0000">
        
                <TextView
                    android:id="@+id/textView1"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Etiqueta 1" />
        
                <TextView
                    android:id="@+id/textView4"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Etiqueta 2" />
        
            </LinearLayout>
         </LinearLayout>


        •  RelativeLayout: este contenedor permite especificar la posicion de cada elemento de forma relativa a su elemento padre, a otro elemento incluido en el mismo layout o incuso a otro layout. En el ejemplo, vemos como el control EditText01 se alinea a la derecha del control TextView01 y el control EditText02 se posiciona debajo y a la derecha del control EditText01.
        <RelativeLayout
                android:id="@+id/relativeLayout1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" >
        
                <TextView
                    android:id="@+id/TextView01"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Nombre:"
                    android:width="100px" />
        
                <EditText
                    android:id="@+id/EditText01"
                    android:layout_width="220px"
                    android:layout_height="wrap_content"
                    android:layout_below="@+id/RelativeLayout01"
                    android:layout_toRightOf="@+id/TextView01" />
        
                <EditText
                    android:id="@+id/EditText02"
                    android:layout_width="220px"
                    android:layout_height="wrap_content"
                    android:layout_alignRight="@+id/EditText01"
                    android:layout_below="@+id/EditText01" />
            </RelativeLayout>

        •  AbsoluteLayout: cada elemento que contenga este contenedor, deberá especificar las coordenadas X e Y en sus propiedades para situar al control dentro de su layout. Este método está obsoleto y se desaconseja su uso.
        • FrameLayout: indicado para contener un único elemento, ya que posiciona los controles a partir de su esquina superior izquierda uno encima del otro.
        <FrameLayout
                android:id="@+id/frameLayout1"
                android:layout_width="wrap_content"
                android:layout_height="match_parent" >
        
                <Button
                    android:id="@+id/button1"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Button" />
        
                <TextView
                    android:id="@+id/textView1"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Large Text"
                    android:textAppearance="?android:attr/textAppearanceLarge" />
            </FrameLayout>
        


        • TableLayout: contenedor basado en tabla. Se pueden añadir filas (TableRow) pero no columnas, es decir, las columnas las forman los controles que vamos añadiendo a las filas (un control, una columna). El tamaño de la columna suele corresponder al tamaño que tenga el control insertado, aunque hay propiedades del TableLayout que nos ayudan a manejar estos parámetros:
          • android:shrinkColumns: indica que columnas pueden contraerse cuando no hay sítio suficiente para mostrar todas las columnas en pantalla y se nos ocultan por la derecha.
          • android:stretchColumns: indica que columnas pueden expandirse si hay sitio suficiente a la derecha del control.
          • android:collapseColumns: indica que columnas queremos ocultar.
          • android:layout_span: análogo al atributo Colspan de HTML. Indica cuantas celdas contiguas ocupará la celda a la que se le aplique.
         Estas propiedades aceptan una lista de índices indicando las columnas afectadas, aunque también podemos hacer referencia a todas las columnas pasándole un asterisco (*).

        <TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/tableLayout1"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:collapseColumns="1"
            android:shrinkColumns="0"
            android:stretchColumns="0" >
        
            <TableRow
                android:id="@+id/tableRow1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" >
        
                <Button
                    android:id="@+id/button1"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Button" />
        
                <Button
                    android:id="@+id/button2"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Button" />
        
                <Button
                    android:id="@+id/button3"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Button" />
            </TableRow>
        
            <TableRow
                android:id="@+id/tableRow2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" >
        
                <CheckBox
                    android:id="@+id/checkBox1"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:keepScreenOn="false"
                    android:text="CheckBox" />
            </TableRow>
        
            <TableRow
                android:id="@+id/tableRow3"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" >
        
                <Spinner
                    android:id="@+id/spinner1"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_span="2" />
            </TableRow>
        
        </TableLayout>
        



        Maikel.

        jueves, 22 de diciembre de 2011

        Primera aplicación sencilla

        Vamos a mostrar los pasos para crear una primera aplicación sencilla donde introduzcamos un nombre en una caja de texto y mediante un botón nos aparezca otra pantalla con el nombre introducido.

        Lo primero es crear un proyecto nuevo en eclipse, para ello pulsamos Ctrl+N o pinchamos en File --> New --> Other, apareciendo el asistente de creación de proyectos. Seleccionamos Android Project y nos aparecerá otra ventana donde pondremos el nombre de la aplicación y el target que queramos.









        A la hora de seleccionar el Target, vemos que para una misma plataforma podemos elegir el Target de Android o de Google APIs. ¿Cual es la diferencia? Pues si pensamos usar en nuestro proyecto Google Maps u otra librería específica de Google, deberíamos elegir el Target Google APIs. Si no vamos a usar nada de esto, seleccionaremos el Target de Android, que sólo incluye librerías del core de Android.







        En la última pantalla de configuración, nos pide que le introduzcamos el nombre del paquete y el nombre de la actividad inicial. En Java es muy comun poner el nombre del dominio en el nombre del paquete pero al reves, así que vamos a ello.









        Con esto, ya tendremos creado nuestro proyecto listo para codificar.








        Lo primero a realizar es remodelar la actividad principal de nuestra aplicación, ya que por defecto, Eclipse te monta un ejemplo "Hola Mundo" que a nosotros no nos hace falta. Hay que añadir una caja de texto y un botón y debemos hacerlo en el fichero main.xml. Si echamos un vistazo a este fichero, vemos que podemos editarlo en modo diseño o modo texto (yo lo haré por código). Dentro del Layout es donde debemos colocar nuestros controles, ya que un Layaout es un contenedor de controles (ya hablaremos de estos contenedores).
        El código debe quedar así:

        <?xml version="1.0" encoding="utf-8"?>
        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:orientation="vertical" >
        
            <EditText 
               android:id="@+id/txtNombre"
               android:layout_width="fill_parent"
               android:layout_height="wrap_content"
                />
            
            <Button android:id="@+id/btnAceptar"
                android:text="@string/textoBoton"
                android:layout_width="wrap_content"
               android:layout_height="wrap_content"
                />
        
        </LinearLayout>
        

        Vamos por partes:

        • La propiedad ID: con la sintaxis @+id, estamos indicando que cuando compile, nos genere en la clase R.java una nueva constante referente al control que podremos usar más adelante en nuestro código.

        • La propiedad Text: con la sintaxis @string, estamos indicando que la cadena de texto la coja del fichero de constantes string.xml. También podemos poner el texto directamente mediante un string.

          Una vez conseguido esto, vamos a crear la pantalla que recojerá la cadena de texto y la mostrará por pantalla. Para ello, pulsamos Ctrl+N y nos aparece el asistente para crear nuevo elemento. Dentro de la carpeta Android, seleccionamos el componente Layout, ya que nosotros queremos crear una pantalla nueva.









          Luego de damos un nombre a nuestra nueva pantalla, seleccionamos que elemento root queremos implementar (seleccionamos LinearLayout para que nos cree el contenedor) y le damos a finalizar.












          En nuestra nueva pantalla sólo vamos a crear una etiqueta que muestre lo introducido en la caja de texto anterior. Debe quedarnos algo así:

          <?xml version="1.0" encoding="utf-8"?>
          <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:orientation="vertical" >
          
              <TextView 
                  android:id="@+id/lblMensaje"
                  android:layout_width="fill_parent"
                  android:layout_height="wrap_content"
                  />
          
          </LinearLayout>


            Con esto ya tenemos nuestras dos interfaces gráficas o Layouts. Ahora toca codificar lo que queremos que haga la aplicación cuando introduzcamos una palabra en la caja de texto y pulsemos el botón. Por tanto, nuestra clase ActividadInicial debe contener las siguientes líneas:

            • Declaramos las dos variables que harán referencia a nuestros controles.
            • private EditText txtCadena;
               private Button btnAceptar;
              

            • Referenciamos las variables a los controles.
            • txtCadena = (EditText) findViewById(R.id.txtNombre);
                btnAceptar = (Button) findViewById(R.id.btnAceptar);
              

            • Creamos el evento OnClick del botón.
            • btnAceptar.setOnClickListener(new OnClickListener() {
                 public void onClick(View v) {
                  pasarStringactividadFinal();
                 }
                });
              

            • Creamos la función llamada desde el evento que contiene la lógica para llamar a la siguiente actividad.
            protected void pasarStringactividadFinal() {
              // establecemos un texto por defecto si no tiene nada
              if (txtCadena.getText().length() == 0) {
               txtCadena.setText(R.string.txtDefecto);}
              // Creamos el Intent que llamará de una actividad a otra.
              // Como parámetros, para este constructor, necesitamos pasarle
              // el contexto actual y la clase de destino
              Intent intent = new Intent(this, ActividadFinal.class);
              // Creamos un objeto Bundle que es parecido a un Hash-Table o
              // tabla de clave-valor y le insertamos el valor a pasar
            //mediante la propiedad putString(clave,valor)
              Bundle ht = new Bundle();
              ht.putString("Parametro", txtCadena.getText().toString());
              // Añadimos la información al Intent mediante la propiedad
            //putExtras(bundle)
              intent.putExtras(ht);
              // Llamamos a la nueva actividad pasándole el intent
              startActivity(intent);
             }
            }
            

            La clase ActividadFinal debe contener la siguiente información:

            • Heredar de la clase Actividad y crear el evento onCreate enlazando mediante el
              ContentView el layout correspondiente (en este caso muestratextbox.xml).
            • public class ActividadFinal extends Activity {
               /** Called when the activity is first created. */
               @Override
               public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.muestratextbox);
              
               }
              }
              
              

            • Mostramos por pantalla la cadena pasada
            • //Referenciamos el control para establecerle el texto
                TextView lblContenedor = (TextView)findViewById(R.id.lblMensaje);
                //Instanciamos un objeto Bundle para extraer los valores 
                //pasados desde la pantalla anterior
                Bundle ht = getIntent().getExtras();
                //Extraemos el valor de la clave correspondiente mediante la 
                //propiedad getString() y la enlazamos a la etiqueta para mostrarla por pantalla
                lblContenedor.setText("El texto introducido es " + ht.getString("Parametro")); 
               
              Por último, solo nos queda añadir en el fichero AndroidManifest.xml la nueva actividad creada.
              <activity android:name=".ActividadFinal"></activity>

            Entrada dedicada a Carmen Delgado, a ver si se pone pronto a programar ;-)

            Maikel.

            viernes, 16 de diciembre de 2011

            ¿Qué es eso de AC2DM?

            Las siglas de Android Cloud To Device Messaging (mensajería de la nube al dispositivo) hacen referencia al sistema por el cual Google conecta con las aplicaciones instaladas en el teléfono y le comunica que tiene datos para consumir. 

            Cuando tenemos una aplicación la cual debe actualizarse periodicamente, esta debe conectarse cada intervalo de tiempo al servidor de donde obtenga la información, preguntar si tiene alguna novedad y, si la tiene, descargarla (esto se conoce como Polling). ¿Qué pasa si no hay nada que descargar? Pues que estamos consumiendo batería y datos.

            A partir de la versión 2.2 de Android, Google ha introducido un nuevo sistema de comunicación entre el servidor y los dispositivos. Este nuevo sistema se conoce como mensajes Push, donde el servidor es el que conecta con el dispositivo cuando hay datos para la aplicación y le dice que se conecte al servidor para descargarlos. Con esto, nos aseguramos que las conexiones sólo son realizadas cuando realmente son necesarias.
            Otra ventaja que nos ofrece, es que la aplicación puede no estar ejecutándose en ese momento, ya que este mensaje push lanzara un Intent que levantará la aplicación. Tampoco necesitamos crearle interfaz gráfica a la hora de recibir la información, pero si queremos podemos crearle una o implementar alguna notificación en la barra de notificaciones.

            Para poder usar este servicio, es necesario registrarse en este enlace.

            Por ahora es una pequeña pincelada de algo que tiene muy buena pinta. Intentaremos trastear un poco y colgar alguna aplicacion de ejemplo.

            Maikel