Manejo de la pantalla táctil con multi-touch

Las pantallas táctiles más modernas tienen la posibilidad de indicar la posición de varios punteros sobre la pantalla a un mismo tiempo. Para averiguar si el dispositivo tiene esta capacidad puedes utilizar el siguiente código:

 

boolean multiTouch = getPackageManager().hasSystemFeature( PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH);

 

A partir de la versión 2.0 (API level 5), un objeto de la clase MotionEvent contendrá información de todos estos punteros. Un puntero estará activo desde que se pulsa sobre la pantalla hasta que se deja de presionar. El número de punteros activos puede consultarse llamando al método getPointerCount(). Cada puntero tiene un id para identificarlo que es asignado cuando se produce la primera pulsación.

La clase MotionEvent amplia la lista de constantes para identificar las acciones posibles para adaptarse a multi-touch a partir de la versión 2.0. Veamos una lista:

 

ACTION_DOWNSe pulsa en la pantalla sin haber otro puntero activo

ACTION_UPSe deja de presionar el último puntero activo

ACTION_MOVECualquiera de los punteros activos se desplaza

ACTION_CANCEL– Se cancela un gesture.

ACTION_OUTSIDE– El puntero se sale de la vista

ACTION_POINTER_DOWN– Se pulsa un nuevo puntero distinto al primero.

ACTION_POINTER_UP– Se deja de presionar un puntero pero no es el último

Ejercicio paso a paso: Uso de la pantalla táctil multi-touch

1. Ejecuta el ejercicio anterior en un dispositivo real con capacidad de multi-touch (si no dispones de uno te será imposible realizar este ejercicio).

2. Pulsa simultáneamente con dos dedos en la pantalla: Si lo haces sin desplazar los dedos recibirás 4 eventos. Los dos primeros por las pulsaciones de cada dedo y los dos siguientes cuando se levanten. El resultado puede ser simular al siguiente:

3. Como puedes ver cuando hay más de un puntero en pantalla la acción resulta compleja de interpretar. Veremos como hacerlo a continuación.

4. En primer lugar asegúrate que tu proyecto utiliza las APIs 2.0 o superior. Para ello utiliza la opción de menú Project/Properties/Android y selecciona en Project Build Target la opción Android 2.0.

5. Reemplaza la siguiente línea del método onTouch()


salida.append(evento.toString()+"\n");
 

por:

String acciones[] = { "ACTION_DOWN", "ACTION_UP", "ACTION_MOVE", "ACTION_CANCEL","ACTION_OUTSIDE", "ACTION_POINTER_DOWN", "ACTION_POINTER_UP" };

int accion = evento.getAction();

int codigoAccion = accion & MotionEvent.ACTION_MASK;

salida.append(acciones[codigoAccion]);

for (int i = 0; i < evento.getPointerCount(); i++) {

salida.append(" puntero:" + evento.getPointerId(i) + 
" x:" + evento.getX(i) + " y:" + evento.getY(i));

}

salida.append("\n");

return true;

6. Para visualizar cada posible acción hemos creado un array con sus nombres. A continuación averiguamos la acción en la variable accion. Esta nueva acción la ha podido hacer cualquier puntero de los activos o uno nuevo. A partir de la versión 2.0 en esta variable se codifica simultáneamente el código de la acción (8 bits menos significativos) e índice de puntero que la ocasiona (siguientes 8 bits). Para obtener esta información por separado puedes utilizar el siguiente código:

int codigoAccion = accion & MotionEvent.ACTION_MASK;

int iPuntero = (accion & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT;

7. A partir de la versión 2.2 (API level 8) las dos últimas constantes quedan obsoletas y se definen otras dos equivalentes cuyos nombres son más adecuados:

int iPuntero = (accion & MotionEvent.ACTION_POINTER_INDEX_MASK)
                                             >> MotionEvent.
ACTION_POINTER_INDEX_SHIFT;

8. Una vez obtenido el código de la acción mostramos su nombre en la vista salida. Luego hacemos un bucle para mostrar información de todos los punteros activos. El método getPointerCount() nos permite averiguar su número. Vamos a recorrer los punteros activos con la variable i. Al principio de este apartado vimos una serie de métodos para averiguar información sobre el puntero (getX(), getSize(),…). A partir de la versión 2.0 estos métodos siguen dándome información sobre el primer puntero activo que se pulsó, pero ahora también disponemos de los mismos métodos pero indicando un índice de puntero (getX(i), getSize(i),…) para averiguar información del resto de punteros.

9. El método getPointerId(int indice)nos permite averiguar el identificador del puntero. No hay que confundir el índice de puntero con su identificador. El índice se asigna en función del orden en que fueron pulsados. El índice cero siempre es el más antiguo. El índice de un puntero decrece a medida que los punteros anteriores a él dejan de estar activos. Por el contrario, el identificador de un puntero es asignado cuando se crea y permanece constante durante toda su vida. Nos será muy útil para seguir la pista de un determinado puntero. El método findPointerIndex(int id)nos permite averiguar el índice de un puntero a partir de su identificador.

10. Ejecuta de nuevo el proyecto y vuelve a pulsar con dos dedos. El resultado ha de ser similar al siguiente:

11. Prueba con otras combinaciones de pulsaciones e investiga la relación entre el índice y el id de puntero.

12. Modifica el programa para que además se muestre en cada evento, el índice de puntero que lo ocasionó.