Feb
25

Cuando se desarrollan aplicaciones de una determinada envergadura nos encontramos con un “problema” durante su desarrollo, y es que, si bien los que nos dedicamos a crear sus funcionalidades y características, solemos tener el entorno de desarrollo instalado en nuestros equipos y podemos compilar código, otros equipos que trabajen en el mismo proyecto, no lo suelen tener.

Ésta es una limitación relativa, ya que en aplicaciones web y de escritorio no suele suponer un gran problema (basta con distribuir la dirección o el binario), pero cuando estamos hablando de aplicaciones móviles, la cosa cambia, sobre todo si no se tienen suficientes dispositivos móviles de cada uno de los tipos para todos los equipos que están trabajando en su desarrollo. Por esto vamos a intentar hacer la vida un poco más fácil a la gente escribiendo un tutorial paso a paso para instalar el emulador de Android (ADB) en equipos MAC OSX (en principio el proceso es similar para todos los sistemas operativos simplemente descargando el ejecutable correcto).

PASO 1: Descargarnos los binarios para realizar la instalación; para ello nos dirigimos a http://developer.android.com/sdk/index.html y descargamos, en este caso, el fichero para MAC OSX.

PASO 2: Descomprimimos el fichero descargado (En caso de haber descargado el ejecutable para Windows, puedes saltarte este paso).

PASO 3: Movemos la carpeta que hemos descomprimido a “/Applications/” (Este paso no es completamente necesario, pero nos vendrá bien para mantener todos los ficheros de nuestro sistema en orden).

PASO 4: Abrimos un “Terminal” (sí, esa pantallita que nos deja hacer de todo con nuestros equipos escribiendo pequeños comandos).

PASO 5: Nos dirigimos a la carpeta a la que hemos movido la que hemos descomprimido, en mi caso, la carpeta se llama “android-sdk-macosx”:

cd /Applications/

cd ./android-sdk-macosx/tools

PASO 6: Ejecutamos en consola el fichero llamado “android

./android

Con esto ejecutaremos un programa que, una vez cargado, presentará un aspecto similar al siguiente:

PASO 7: Instalamos los paquetes correspondientes a las plataformas que deseemos ejecutar (yo recomiendo la versión 2.2 +o 2.3, ya que por el momento son las más extendidas en los móviles en circulación)

PASO 8: Aceptamos los términos y condiciones al hacer click en instalar los paquetes seleccionados y esperamos a que el proceso llegue a su fin; este proceso puede tardar varios minutos.

PASO 9: Una vez instalados los paquetes deseado, nos dirigimos al menú contextual y hacemos click en “Tools” y en el menú que se despliega seleccionamos “Manage AVDs“, con lo que nos aparecerá una ventana similar a la siguiente:

PASO 10: Crear un dispositivo virtual: para ello hacemos click en “New…“, le damos un nombre y seleccionamos las características que deseemos, como por ejemplo las siguientes:

Hacemos click en “Create AVD

PASO 11: Seleccionamos el AVD que acabamos de crear y pulsamos en “Start“; opcionalmente, en la ventana que aparece, en lugar de darle a aceptar directamente, podemos modificar las dimensiones de la pantalla entre otros parámetros, pero eso ya os dejo explorarlo a vosotros.

Como resultado ya tenemos a nuestra disposición un emulador de Android completamente funcional y que nos permite simular desde posiciones de GPS hasta la cámara de fotos,…

Categoría Android, Manuales | 2 Comentarios »
Feb
23

Hace una semana comentábamos el modo de crear un ListView para Android con, como mínimo, un CheckBox en cada fila de la lista. Este mini tutorial de hoy, en el que vamos a explicar como marcar de una todos los CheckBox de un ListView, vamos a basarnos en el contenido explicado en dicho artículo.

En principio la metodología parece sencilla, en nuestro fichero main.xml, además de declarar un ListView, declaramos en la parte inferior un CheckBox cuyo comportamiento determinaremos más adelante, de manera que el contenido del main.xml quedaría de la siguiente manera:

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <ListView
    	android:id="@+id/list"
    	android:layout_width="fill_parent"
    	android:layout_height="wrap_content"
        android:layout_weight="1"
        android:drawSelectorOnTop="false"/>
	<CheckBox
		android:id="@+id/chkSelectAll"
		android:text="@string/chkSelectAll"
		android:visibility="visible"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"/>
</LinearLayout>

Hasta aquí todo ha sido coser y cantar, de nuevo hemos de definir un fichero con el Layout de cada una de las filas de lo que será nuestro ListView, en nuestro caso item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_height="wrap_content"
	android:layout_width="fill_parent">
	<CheckBox
		android:id="@+id/chkItem"
		android:visibility="visible"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"/>
</LinearLayout>

Una vez que tenemos hecho esto nos bastará con definir el comportamiento de la clase principal para que al marchar el chkSelectAll se marquen todos los CheckBox de la lista, para ello en un principio, con poner el array que determina el valor de cada CheckBox a true o false, en función del caso, debería sernos suficiente. Para ello añadiríamos en el onCreate las liguientes lineas:

        chkAll = (CheckBox)findViewById(R.id.chkSelectAll);
        chkAll.setOnCheckedChangeListener(new OnCheckedChangeListener(){
			@Override
			public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
				for (int i = 0; i < adapter.getItemsLength(); i++) {
    	                                adapter.set(i, b);
    	                        }
			}
        });

El problema nos lo encontramos cuando lo que queremos es que además de marcarlos en el sistema queremos que se visualicen los CheckBox marcados directamente, para ello, lo que podemos hacer (AVISO: es una solución un poco cutre y simplemente para salir del paso) es reasignar el adaptador, para lo que después del for podríamos añadir las siguientes lineas:

        int pos = list.getFirstVisiblePosition();
    	list.setAdapter(adapter);
    	list.setSelection(pos);

Si ahora compilamos y ejecutamos el código podremos observar como el comportamiento es el deseado.

Categoría Android, Manuales, Programación | Sé el primero en comentar!
Feb
16

Android es un gran sistema operativo para móviles que le supone una competencia directa al iOS, y por lo tanto meterse como programador en su mundo puede ser algo muy atractivo. Pero como programadores más o menos experimentados sabemos que no todo es un paseo, tal y como algunos pretenden plantearlo; siempre surgen problemas que habremos de solucionar. Hoy, vamos a ver como evitar que, al tener un ListView en el que cada fila contenga un CheckBox, al hacer scroll, los elementos de la parte no visible se marquen al marcar uno de la parte visible de manera automática.

¿Por qué?

Esto sucede porque Android reutiliza las mismas vistas una y otra vez a la hora de pintar los elementos en pantalla, de manera que aunque el CheckBox que ha de mostrar no está seleccionado, se mostrará como tal dado que uno visible anteriormente ya lo estaba. De este modo se ahorra una gran cantidad de ciclos de CPU a la hora de generar la imagen que se va a mostrar.

Cómo podemos ver, el comportamiento por defecto del sistema operativo no siempre nos viene bien ya que, en casos como este, puede dar lugar a confusiones en el usuario final de la aplicación.

Solución

La manera de solucionar este pequeño inconveniente, aunque no de la forma más óptima, es tan simple como establecer a mano el valor del CheckBox que ha de mostrarse en cada momento, para ello tendremos que apoyarnos en una serie de clases y ficheros XML.

Antes de nada, vamos a analizar los ficheros que componen nuestro Layout, es decir, el que establece la distribución de la pantalla principal y el que establece el aspecto de cada una de las filas.

main.xml

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

row.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="fill_parent">
<CheckBox
android:id="@+id/chkItem"
android:visibility="visible"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</LinearLayout>

Tal y como se puede observar, hasta este momento no hemos hecho más que definir dos layouts, uno que contiene un ListView y otro que contiene el CheckBox que ha de aparecer en cada fila.

Prosigamos programando nuestra clase Java para manejar estos Layouts:

package org.dipler.chkList;

import java.util.ArrayList;

import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ListView;
import android.widget.CompoundButton.OnCheckedChangeListener;

public class Main extends Activity {
private ChkListAdapter adapter;
private ListView list;
private CheckBox chkAll;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
list = (ListView)findViewById(R.id.list);
fillList();
}

private void fillList() {
int num = 50;
adapter = new ChkListAdapter(num);
for(int i = 0; i < num; i++){
adapter.addItem(new Item(String.valueOf(i), "item number " + i));
}
list.setAdapter(adapter);
}

private class ChkListAdapter extends BaseAdapter {
private ArrayList<Item> items = new ArrayList<Item>();
private LayoutInflater inflater;
private boolean[] itemSelection;
public ChkListAdapter(int size) {
inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.itemSelection = new boolean[size];
}

public void addItem(final Item item) {
items.add(item);
notifyDataSetChanged();
}

@Override
public int getCount() {
return items.size();
}

@Override
public String getItem(int position) {
return items.get(position).toString();
}

@Override
public long getItemId(int position) {
return position;
}

@Override
public View getView(final int position, View convertView, ViewGroup parent) {
convertView = inflater.inflate(R.layout.row, null);
final ViewHolder holder = new ViewHolder();
holder.chkItem = (CheckBox)convertView.findViewById(R.id.chkItem);
holder.chkItem.setOnCheckedChangeListener(new OnCheckedChangeListener(){
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
itemSelection[position] = holder.chkItem.isChecked();
}
});

holder.chkItem.setChecked(itemSelection[position]);
convertView.setTag(holder);
holder.chkItem.setText(getItem(position));
return convertView;
}

public int getItemsLength() {
if(itemSelection == null) return 0;
return itemSelection.length;
}

public void set(int i, boolean b) {
itemSelection[i] = b;
}

}

public static class ViewHolder {
public CheckBox chkItem;
}

}

Como podemos observar el la, bueno, las clases Java que hemos declarado tenemos:

  • La clase Activity principal que utiliza el Layout main.xml y que en el método onCreate() rellena el ListView con elementos, en nuestro caso de tipo Item (Item es una clase cualquiera con dos atributos, un id y un texto)
  • El adapter, que es la clase que se va a dedicar a repintar las filas tantas veces como sea necesario llamando al método getView() en cada una de esas ocasiones.
  • La clase ViewHolder que nos facilita el trabajo con la clase Row, esta clase no es en sí necesaria, pero vi la idea en amberfog y me gustó, de hecho de esta misma página obtuve una visión mucho más clara del funcionamiento de estos artificios llamados “Views”

El procedimiento para evitar que se marquen solos los CheckBox que cumplen el periodo, es sencillo, llevamos un array de booleanos en el que establecemos el estado de cada elemento de la lista confome cambia el valor del CheckBox, de manera que cada vez quedeseemos mostrar dicho elemento, el método getView(), en lugar de pintar como cree que debería pintar estos CheckBox, los muestra marcados o desmarcados en función del array que hemos creado y que vamos actualizando dinámicamente.

El método propuesto no es el más eficiente que nos podamos encontrar, pero funciona y nos puede ayudar a salir del paso.

Categoría Android, Manuales, Programación | 24 Comentarios »
Feb
9

Muchos son los manuales que a día de hoy nos “enseñan”, o al menos eso dicen, a publicar con todo detalle los pasos para publicar nuestras aplicaciones para android en el market, y no digo que no sea cierta dicha aseveración, sino que, además deberían contar algunos inconvenientes a la hora de hacerlo, vamos a intentar proponer estos baches que a mi me han hecho perder un poco de tiempo para que no tengáis que perderlo vosotros también.

Este artículo parte de que todo lo que pone en el resto de páginas lo sabéis y no tenéis demasiado problema a la hora de hacer (en caso de dudas, preguntad sin temor en los comentarios de este artículo).

Lo más probable, es que si estás leyendo este artículo es que desarrolles aplicaciones para dispositivos con una versión de Android instalada superior a la 1.5.

A la hora de publicar para estas versiones aplicaciones hemos de tener en cuenta varios parámetros de fichero AndroidManifest.xml y, es que, para que se publique en el android para todos los dispositivos tendremos que decirlo expresamente en este fichero:

<manifest ...>
<uses-sdk android:minSdkVersion="4" />
 <supports-screens
 android:largeScreens="true"
 android:normalScreens="true"
 android:smallScreens="true"
 android:anyDensity="true" />

En estas líneas estamos diciendo que independientemente del tipo de pantalla de nuestro dispositivo, la aplicación va a ser compatible. Si no incluimos estas líneas, veremos como nuestra aplicación aparece en el Market para pantallas normales y grandes, pero no para pequeñas, es decir, en mi caso, la aplicación no se mostraba para mi HTC Wildfire, dispositivo en el que había estado haciendo todo el proceso de depurado de la aplicación.

Por otra parte, si deseamos publicar una aplicación de pago, deberemos tener una empresa registrada para darla como dato en la página de Google Checkout, la cual nos serviría para recibir pagos. Como alternativa recomiendo hacer uso de sistemas publicitarios en caso de que deseemos sacar algo de dinero de la aplicación y no tengamos empresa a la que asociar nuestra cuenta.

Categoría Android, Manuales | Sé el primero en comentar!