Juanma Santoyo

En ocasiones me llaman friki

Detección y gestión de contactos en una Surface

| 1 Comentario

A estas alturas de la película, uno ya debería tener claro que la base de las entradas de usuario en una Surface son los contactos.

Da igual si tocamos la pantalla con un dedo o con toda la mano, con un trozo de madera; o si hemos colocado un elemento etiquetado: Todo se reduce a detectar un contacto y gestionarlo.

Por lo tanto, se podría decir que hay varios tipos de contactos. En este artículo vamos a ver qué tipos de contactos hay, cómo podemos detectarlos, y cómo podemos obtener información relevante sobre los mismos. No es un asunto complicado, pero sí es bastante amplio. A modo de resumen, en el artículo vamos a tratar los siguientes puntos:

  1. Tipos de contactos.
  2. Reconocimiento de contactos.
  3. Diferenciación de contactos.
  4. Reconocer el mismo contacto en diferentes eventos.
  5. Conocer los diferentes contactos que existen sobre un elemento.

Tipos de contactos.

Básicamente, podemos considerar tres tipos diferentes de contactos:

Dedos.

La mayoría de contactos son dedos, ya que para el usuario, lo más natural y intuitivo casi siempre será hacer algo con el dedo sobre la pantalla.

Etiquetas.

Una de las características más interesantes de una Surface es la detección de etiquetas. Las etiquetas son unos gráficos que al entrar en contacto con la pantalla son reconocidos como tales y tienen un valor numérico determinado.

Existen dos tipos de etiquetas: Las Byte Tags y las Identity Tags. La principal diferencia entre ambas es que las Byte Tags pueden tomar 28 valores distintos, y los Identity Tags pueden tomar 2128 valores diferentes (separados en dos campos de 264 y 264).

Byte Tag

Un Byte Tag

Identity Tag

Un Identity Tag

Objetos.

En realidad, la Surface reacciona ante cualquier contacto sobre la pantalla, sea lo que sea. Puede ser el dorso de la mano, un trozo de madera, etc. Por lo tanto, catalogaremos como un “objeto” cualquier contacto que no sea ni un dedo ni una etiqueta.

Reconocer contactos sobre un elemento.

Reconocer los contactos sobre un elemento es relativamente sencillo. El propio SDK nos aporta eventos como “ContactDown“, “ContactUp” o “ContactTapGesture” que nos realizaran dicha tarea. De hecho, “ContactDown” nos servirá en la mayoría de las ocasiones.

Lo que sí requiere más conocimiento del tema es cómo diferenciar los diferentes tipos de contactos, y cómo obtener información relevante sobre cada uno.

Veamos un ejemplo sencillo de “ContactDown“. Por ejemplo, sobre un control de usuario que tengamos en pantalla.

Añadiremos la gestión del evento en el XAML del Control de usuario:

<s:SurfaceUserControl x:Class="DeteccionDeContactos.MyUserControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:s="http://schemas.microsoft.com/surface/2008"

    ContactDown="SurfaceUserControl_ContactDown"
    Width="100" Height="100"
>
    <Grid Background="Red">

    </Grid>
</s:SurfaceUserControl>

Y esto nos generará en el código C# un método como este:

private void SurfaceUserControl_ContactDown(object sender, ContactEventArgs e)
{
}

Podéis ver que por parámetro nos llegan dos objetos. Uno es el objeto sender, que no es más que el control de usuario que ha recogido el evento “ContactDown“. Por otra parte, nos llega el objeto e. Es un objeto del tipo “ContactEventArgs“, y contiene mucha información que nos puede interesar.

Obtener información útil sobre los contactos.

Antes de nada, vamos a ver cómo podemos saber qué tipo de contacto es. En realidad es sencillo, ya que las propiedades e.Contact.IsContactRecognized y e.Contact.IsTagRecognized nos lo revelarán:

private void SurfaceUserControl_ContactDown(object sender, ContactEventArgs e)
{
    if (e.Contact.IsFingerRecognized)
    {
        Debug.WriteLine("Es un dedo.");
    }
    else if (e.Contact.IsTagRecognized)
    {
        Debug.WriteLine("Es un elemento etiquetado.");
    }
    else
    {
        Debug.WriteLine("Es un objeto.");
    }
}

Ahora bien: ¿Más cosas que nos puedan interesar? Pues dependerá del tipo de contacto, obviamente. En general, algo que tienen en común todos los contactos es una posición en pantalla y una orientación (recordemos que una de las más geniales características de una Surface es poder detectar la orientación de un contacto). Nos lo dirán los métodos e.Contact.GetPosition y e.Contact.GetOrientation. Ambos métodos reciben por parámetro un elemento respecto al que orientarse. Lo más común es orientarse respecto al objeto que capturó el método, así que el parámetro sender que mencioné antes nos viene de perlas. El objeto sender en principio viene tipado como un objeto genérico, así que habrá que hacerle un casting. No debería ser un problema si tenemos claro quien lanzó el evento.

private void SurfaceUserControl_ContactDown(object sender, ContactEventArgs e)
{
    MyUserControl uc = (MyUserControl) sender;

    Point position = e.GetPosition(uc);
    Double orientation = e.Contact.GetOrientation(uc);

    Debug.WriteLine("El contacto se ha producido en la posición: {0}, {1}.", position.X, position.Y);
    Debug.WriteLine("La orientación del contacto es de {0} grados.", orientation);

    if (e.Contact.IsFingerRecognized)
    {
        Debug.WriteLine("Es un dedo.");
    }
    else if (e.Contact.IsTagRecognized)
    {
        Debug.WriteLine("Es un elemento etiquetado.");
    }
    else
    {
        Debug.WriteLine("Es un objeto.");
    }
}

Bueno, ya sabemos dónde se produjo el contacto, y sabemos también su orientación. Si el contacto fuese un dedo, no necesitamos saber más. Pero ¿Y si es un objeto etiquetado? Hay dos parámetros importantísimos que nos interesará conocer: el tipo de etiqueta y su valor. Conviene anticipar que si el contacto es de tipo Identity, el valor se separa en dos campos: la serie y el valor. La serie son los primeros 264 bits, el valor son los últimos 264 bits. Debemos considerar el objeto e.Contacts.Tag y sus propiedades.

private void SurfaceUserControl_ContactDown(object sender, ContactEventArgs e)
{
    MyUserControl uc = (MyUserControl) sender;

    Point position = e.GetPosition(uc);
    Double orientation = e.Contact.GetOrientation(uc);

    Debug.WriteLine("El contacto se ha producido en la posición: {0}, {1}.", position.X, position.Y);
    Debug.WriteLine("La orientación del contacto es de {0} grados.", orientation);

    if (e.Contact.IsFingerRecognized)
    {
        Debug.WriteLine("Es un dedo.");
    }
    else if (e.Contact.IsTagRecognized)
    {
        Debug.WriteLine("Es un elemento etiquetado.");

        switch (e.Contact.Tag.Type)
        {
            case TagType.Byte:

                Byte byteTagValue = e.Contact.Tag.Byte.Value;

                Debug.WriteLine("Es un Byte Tag. Su valor es {0}.", byteTagValue);
                break;

            case TagType.Identity:

                long serie = e.Contact.Tag.Identity.Series;
                long identityTagValue = e.Contact.Tag.Identity.Value;

                Debug.WriteLine("Es un Identity Tag. Su serie es: {0}. Su valor es {1}.", serie, identityTagValue);
                break;
        }
    }
    else
    {
        Debug.WriteLine("Es un objeto.");
    }
}

Por último, si el contacto es un objeto cualquiera, nos interesará saber su forma y el área que ocupa. El propio SDK nos ayuda creando un objeto Ellipse que tiene la forma aproximada del contacto. Nosotros podemos saber dónde está el centro del elipse, cuál es su tamaño y incluso el área exacta del objeto que toca la pantalla. Debemos observar los métodos e.Contact.GetEllipsee.Contact.GetCenterPosition, y la propiedad e.Contact.PhysicalArea. Los métodos para obtener los valores de la elipse también reciben por parámetro el objeto respecto al que deben calcularse. Igual que antes, lo normal sería usar el parámetro sender.

private void SurfaceUserControl_ContactDown(object sender, ContactEventArgs e)
{
    MyUserControl uc = (MyUserControl) sender;

    Point position = e.GetPosition(uc);
    Double orientation = e.Contact.GetOrientation(uc);

    Debug.WriteLine("El contacto se ha producido en la posición: {0}, {1}.", position.X, position.Y);
    Debug.WriteLine("La orientación del contacto es de {0} grados.", orientation);

    if (e.Contact.IsFingerRecognized)
    {
        Debug.WriteLine("Es un dedo.");
    }
    else if (e.Contact.IsTagRecognized)
    {
        Debug.WriteLine("Es un elemento etiquetado.");

        switch (e.Contact.Tag.Type)
        {
            case TagType.Byte:

                Byte byteTagValue = e.Contact.Tag.Byte.Value;

                Debug.WriteLine("Es un Byte Tag. Su valor es {0}.", byteTagValue);
                break;

            case TagType.Identity:

                long serie = e.Contact.Tag.Identity.Series;
                long identityTagValue = e.Contact.Tag.Identity.Value;

                Debug.WriteLine("Es un Identity Tag. Su serie es: {0}. Su valor es {1}.", serie, identityTagValue);
                break;
        }
    }
    else
    {
        Double area = e.Contact.PhysicalArea;
        Ellipse ellipse = e.Contact.GetEllipse(uc);
        Point ellipseCenter = e.Contact.GetCenterPosition(uc);

        Debug.WriteLine("Es un objeto.");
        Debug.WriteLine("La elipse aproximada que contiene el contacto es ancho: {0}, alto: {1}", ellipse.Width, ellipse.Height);
        Debug.WriteLine("El punto central de la elipse es: {0}, {1}", ellipseCenter.X, ellipseCenter.Y);
    }
}

Reconocer el mismo contacto en diferentes eventos.

Los contactos en realidad disparan más de un evento. Por ejemplo, un mismo contacto primero genera un “ContactDown” y por último generará un “ContactUp“. Entonces, ¿Cómo saber que son el mismo?. Para ello usaremos la propiedad e.Contact.Id. El mismo contacto, en diferentes eventos; conservará el mismo Id.

Conocer los diferentes contactos sobre un elemento.

Y para acabar, algo que también nos puede ser de utilidad es conocer los diferentes contactos que hay sobre un elemento. Para ello, podemos usar la propiedad ContactsOver que nos proporcionan los controles de usuario. Esto es una lista de objetos Contact, por lo que a cada elemento se le podrían aplicar todas las propiedades y métodos vistos en este artículo.

Hemos acabado.

Bueno, acabamos de pegar un buen repaso a los contactos de Surface. Dejo la descarga a un ejemplo que desarrollé para escribir este artículo.

Deteccion de contactos (VS 2010)

El contenido de este artículo debería ser suficiente para que cualquier hombre de bien desarrolle aplicaciones bastante decentes para Microsoft Surface, pero el que tenga dudas o necesite más información sobre algún aspecto que no dude en comentarlo por aquí. Yo mismo sigo aprendiendo sobre el tema, así que cualquier ayuda que os pueda prestar también me ayuda a mí.

Share on FacebookTweet about this on TwitterShare on Google+Share on LinkedInEmail this to someone

One Comment

  1. Pingback: Juanma Santoyo » Blog Archive » Reaccionar a los objetos etiquetados en Microsoft Surface: el control TagVisualization.

Deja un comentario

Los campos obligatorios están marcados con *.