Mostrando entradas con la etiqueta Android. Mostrar todas las entradas
Mostrando entradas con la etiqueta Android. Mostrar todas las entradas

miércoles, 23 de agosto de 2017

Pasando datos entre activities (IParcelable, JSON y ISerializable)

Hace unas semanas durante una sesión de apoyo del diplomado de Xamarin en la que estuve como asistente, alguien pregunto cómo podía pasar objetos entre dos Activities en lugar de pasar solamente datos primitivos. En esa ocasión yo me comprometí a hacer un post sobre el tema y aun ya pasaron muchas semanas aquí está el post.

En Android no es posible pasar mediante los extras de un Intent elementos que no sean tipos primitivos que nosotros generemos. Al intentar hacerlo obtendremos el siguiente error.



Existen varias manera de poder pasar objetos, cada una con sus ventajas y desventajas. En este post hablaremos de 4, las cuales son las siguientes:

1. Usar elementos estáticos: 
Esta es la forma más fácil pero la menos limpia y es simplemente crear un elemento publico estático en nuestro primer Activity y desde el segundo Activity lo leemos. A este mecanismo no le daremos mucha importancia debido a que es muy simple y en realidad no estamos pasando nada y aunque parezca increíble hace algunos años cuando comencé con Xamarin y me encontré con esta problemática me encontré con esta solución en muchos foros y grupos sobre Android.

2. Serializar (JSON o XML) 
Una segunda manera muy simple es serializar nuestro objeto y enviarlo como si fuera una cadena y luego hacer el proceso inverso en el Activity que recibe. Este método es muy simple, mucho más limpio que el método anterior sin embargo es “costoso” en recursos, para el poder de procesamiento actual probablemente ese no sea un gran problema.

3. Crear un objeto Parcelable. 
Esta es la solución óptima para esta problemática, en Android existe la interfaz Parcelable lo cual en Xamarin se traduce como IParcelable para mantener la nomenclatura de C#. IPacerlable requiere de dos cosas tener un método “WriteToParcel” el cual se encarga de serializar nuestro objeto y tener definido un “Creator” el cuál es el encargado de deserializar el objeto. Este mecanismo es mucho más rápido que serializar a JSON.

4. Serializable 
En Java existe una interfaz con este nombre y al igual que el caso anterior en Xamarin la tenemos disponible con la nomenclatura de C# como ISerializable, este mecanismo es soportado en Android por temas de compatibilidad con Java pero no es recomendado.

Además, existe una gran diferencia entre usarlo en Java y en Xamarin y es que en Java esta interfaz puede hacer uso de Reflection para serializar el objeto, esto hace que el proceso sea costoso a cambio de no tener que implementar la serialización de forma manual como sucede con IParcelable. En Xamarin no tenemos esta opción y si debemos implementar los métodos de la interfaz de manera manual, los métodos son “WriteObject” y “ReadObject” por esta razón el proceso termina siendo casi igual que con IParcelable y eso nos deja prácticamente sin razones para hacer uso de Serializable para el tema que estamos tratando en este post.


Vamos a hacer código


Vamos a revisar cómo implementar cada uno de los escenarios y haremos una pequeña y no muy formal prueba de rendimiento para comparar cómo se comporta cada escenario (Solo dejaremos fuera el uso de elementos estáticos debido a que en la mayoría de los casos si no es que en todos no es recomendable usarlo)


Para el ejemplo usare un proyecto en blanco de Xamarin.Android



Y simplemente agregare un Activity nuevo al proyecto llamado “SecondActivity” y lo dejare tal cual como es generado, es decir no le pondré interfaz gráfica.

A lo largo de los ejemplos trabajaremos con esta clase llamada “Employee”

```
public class EmployeeJson
    {

  public string Name
  {
   get;
   set;
  }


  public JobPosition Position
  {
   get;
   set;
  }



  public string Email
  {
   get;
   set;
  }


  public EmployeeJson(JobPosition position, string name, string email)
  {
   Name = name;
   Position = position;
   Email = email;
  }
}
}

```

1. Serialización con JSON

Comencemos con el más simple de los escenarios, para este caso la clase “Employee” se mantiene intacta y todo el trabajo lo haremos en el Activity.

Primero debemos instalar el paquete NuGet Json.NET



El proyecto por defecto tiene un botón que al ser presionado modifica un contador, haremos uso de ese mismo botón y su evento clic para el ejemplo.

Al final el manejador queda de esta manera

```
button.Click += delegate
            {
    EmployeeJson myEmployeeJson = new EmployeeJson(JobPosition.Operator, "Alan Lopez", "alan@mail.com");
    Intent secondActivityIntentJson = new Intent(this, typeof(SecondActivity));
    secondActivityIntentJson.PutExtra("employee", Newtonsoft.Json.JsonConvert.SerializeObject(myEmployeeJson));
    StartActivity(secondActivityIntentJson);  
   }; 
```
Con esta solución solo utilizamos 4 líneas de código para enviar el elemento (las líneas de Stopwatch son para medir el tiempo), en el segundo Activity en el método OnCreate recibiremos ese elemento serializado y lo regresamos a ser un objeto con la siguiente lógica

```
EmployeeJson myEmployeeJson = Newtonsoft.Json.JsonConvert.DeserializeObject(Intent.GetStringExtra("employee"));
```
En este caso solo con una línea retornamos los datos a una instancia del objeto.

En mi equipo ejecute este código varias veces, siempre cerrando e iniciando de nuevo la app en modo debug y estos son los resultados.


Intento | Escritura| Lectura
---------|----------|---------
 1 | 1245 ms | 159 ms
 2 | 1235 ms | 120 ms
 3 | 1189 ms | 177 ms
 4 | 1199 ms | 160 ms
 5 | 1286 ms | 155 ms

En todos los casos supero los 1000ms en serializar y supero los 120 realizar el proceso inverso.

Como comenté anteriormente es una medición poco formal, simplemente ejecute en el primer emulador (Android 7.1.1 con 2GB de RAM) que se inició y con ese mismo realizare las pruebas a los demás escenarios, todos en las mismas condiciones. (El código está disponible en GitHub al final está el enlace)


2. IParcelable 
Para este escenario creare una nueva clase que implementa IParcelable, esta clase tiene las siguietes características:

- Hereda de Java.Lang.Object, esto debido a que IParcelable implementa las interfaces IJavaObject y también IDisposable, con la clase Object cubrimos los métodos de esas interfaces.
- Implementa el método WriteToParcel, en este metodo serializamos el objeto escribiendo una a una las propiedades en un objeto de tipo Parcel. Aquí es importante el orden en que escribimos las propiedades, el orden en que estén escritas es el orden en que deben leerse cuando regeneremos el objeto.
- Tiene un método público y estático llamado InititalizeCreator que retorna un element de tipo EmployeeParcelableCreator. Este método es el que nos devuelve la instancia del objeto a la hora de deserializarlo. En Android debemos definir un método llamado CREATOR que sea publico estatico que implemente la interfaz “Parcelable.Creator”, en Xamarin usamos el atributo [ExportField("CREATOR")] (dentro del namespace Java.Interop) para indicar que este es el método que requiere IParcelable, también en nuestro caso el método retorna un objeto que implementa la interfaz “IParcelableCreator”, el cual explico más adelante.
*El atributo export sirve para hacer que un método de código manejado pueda ser invocado desde Android, como parámetro le indicamos el nombre con el que lo exponemos. A esto se le llama un método Android Callable Wrapper (ACW)

Así queda definida nuestra clase EmployeeParcelable

```
public class EmployeeParcelable: Java.Lang.Object, IParcelable
    {
  public string Name
  {
   get;
   set;
  }

 
  public JobPosition Position
  {
   get;
   set;
  }

  
 
  public string Email
  {
   get;
   set;
  }

       
        public EmployeeParcelable(JobPosition position, string name,  string email)
  {
   Name = name;
   Position = position;
   Email = email;
  }

        public int DescribeContents()
        {
            return 0;
        }

        public void WriteToParcel(Parcel dest, [GeneratedEnum] ParcelableWriteFlags flags)
        {
            dest.WriteInt((int)Position);
            dest.WriteString(Name);
            dest.WriteString(Email);
        }

  [ExportField("CREATOR")]
        public static EmployeeParcelableCreator InititalizeCreator()
  {
   return new EmployeeParcelableCreator();
  }
    }
```

Nuestra otra clase necesaria es EmployeeParcelableCreator, esta tiene las siguientes características:

- Hereda de Java.Lang.Object por las razones mencionadas en la clase anterior, e implementa IParcelableCreator.
- Implementa el método CreateFromParcel que debe retornar un objeto del tipo desde el cual es llamado este objeto, para este ejemplo un “EmployeeParcelable”.
- Es importante que leamos los datos en el orden en que los escribimos en el método WriteToParcel, para este ejemplo los escribi y los leo en el orden en que los espera el constructor del objeto “EmployeeParcelable”
- La interfaz nos obliga a implementar el método “NewArray” pero ese es un tema que no entra en el alcance de este post por lo cual lo dejaremos con una implementación simple.

```
public class EmployeeParcelableCreator : Java.Lang.Object, IParcelableCreator
 {
  public Java.Lang.Object CreateFromParcel(Parcel source)
  {
            return new EmployeeParcelable((JobPosition)source.ReadInt(),source.ReadString(), source.ReadString());
  }

  public Java.Lang.Object[] NewArray(int size)
  {
   return new Java.Lang.Object[size];
  }


    }
```
Hasta este punto ya escribimos mucho más que en el ejemplo con JSON, ahora falta hacer uso de esta implementación para enviar los datos entre activities

En nuestro MainActivity enviamos de esta manera

```
button.Click += delegate
            {
             EmployeeParcelable myEmployeeParcelable = new EmployeeParcelable(JobPosition.Operator, "Alan Lopez", "alan@mail.com");
                Intent secondActivityIntentParcelable = new Intent(this, typeof(SecondActivity));
                secondActivityIntentParcelable.PutExtra("employee", myEmployeeParcelable);
                StartActivity(secondActivityIntentParcelable); 
   }; 
```

En el Segundo Activity leemos con la siguiente línea

```
EmployeeParcelable myEmployeeParcelable = (EmployeeParcelable)Intent.GetParcelableExtra("employee"); 

```
Al hacer las pruebas los tiempo de respuesta fueron los siguientes:

Intento | Escritura| Lectura
---------|----------|---------
 1 | 239 ms | 12 ms
 2 | 220 ms | 13 ms
 3 | 253 ms| 14 ms
 4 | 229 ms | 13 ms
 5 | 240 ms| 9 ms


Usando IParcelable el ejemplo se ejecutaba entre 1/4 y 1/5  del tiempo que le tomaba a la solución con JSON

3. ISerializable 
Para este caso crearemos una clase EmployeeSerializable con las siguientes características.

- Implementa ISerializable y hereda de Java.Lang.Object esto ultimo por las mismas razones que IParcelable.
- ISerializable nos obliga a implementar WriteObject y ReadObject al igual que con IParcelable es importante el orden de escritura y de lectura y usamos el atributo Export para exponerlos a Android.
- Además tenemos unos métodos muy simples para poder reutilizar la escritura y lectura de los datos que serializamos dentro de los Stream que se utilizan para este trabajo.

La clase EmployeeSerializable está definida de este modo

```
public class EmployeeSerializable : Java.Lang.Object, ISerializable
    {

  public EmployeeSerializable(IntPtr handle, JniHandleOwnership transfer)
            : base (handle, transfer)
        {
  }

        [Export("readObject", Throws = new[] {
        typeof (Java.IO.IOException),
        typeof (Java.Lang.ClassNotFoundException)})]
        private void ReadObject(Java.IO.ObjectInputStream source)
        {
            Position = (JobPosition)ReadInt(source);
            Name = ReadNullableString(source);
            Email = ReadNullableString(source);
        }

        [Export("writeObject", Throws = new[] {
        typeof (Java.IO.IOException),
        typeof (Java.Lang.ClassNotFoundException)})]
        private void WriteObject(Java.IO.ObjectOutputStream destination)
        {
            WriteInt(destination, (int)Position);
            WriteNullableString(destination, Name);
            WriteNullableString(destination, Email);
        }

  static void WriteNullableString(Java.IO.ObjectOutputStream dest, string value)
  {
   dest.WriteBoolean(value != null);
   if (value != null)
    dest.WriteUTF(value);
  }

  static void WriteInt(Java.IO.ObjectOutputStream dest, int value)
  { 
            dest.WriteInt(value);
  }

  static string ReadNullableString(Java.IO.ObjectInputStream source)
  {
   if (source.ReadBoolean())
    return source.ReadUTF();
   return null;
  }

  static int ReadInt(Java.IO.ObjectInputStream source)
  {
            return source.ReadInt();
  }

        public string Name
        {
            get;
            set;
        }


        public JobPosition Position
        {
            get;
            set;
        }



        public string Email
        {
            get;
            set;
        }


        public EmployeeSerializable(JobPosition position, string name, string email)
        {
            Name = name;
            Position = position;
            Email = email;
        }


    }
```

Para enviar los datos, en el MainActivity el evento clic quedo de esta manera

```
button.Click += delegate
            {
             EmployeeSerializable myEmployeeSerializable = new EmployeeSerializable(JobPosition.Operator, "Alan Lopez", "alan@mail.com");
 Intent secondActivityIntentSerializable = new Intent(this, typeof(SecondActivity));
 secondActivityIntentSerializable.PutExtra("employee", myEmployeeSerializable);
 StartActivity(secondActivityIntentSerializable);
   }; 
```
Para leer en el segundo Activity el código es:

```
EmployeeSerializable myEmployeeSerializable = (EmployeeSerializable)Intent.GetSerializableExtra("employee");

```

Los resultados de tiempo de ejecución son los siguientes:


Intento | Escritura| Lectura
---------|----------|---------
 1 | 267 ms | 22 ms
 2 | 253 ms| 29 ms
 3 | 254 ms| 23 ms
 4 | 236 ms | 25 ms
 5 | 240 ms|  21 ms


Se puede considerar que ISerializable fue ligeramente superior en promedio comparado con IParcelable pero los resultados fueron muy similares en la serialización y un muy poco mas lentos en la lectura.


## Conclusión ##

Después de realizar los ejercicios y ver sus tiempos de ejecución podría decir que si es fundamental el rendimiento lo mejor es usar IParcelable el cual es mucho más rápido que serializar objetos en JSON, a cambio de este rendimiento amarramos nuestras clases a algunos elementos propios de Android, podríamos reducir este impacto mediante el uso de herencia sin embargo esto conlleva más tiempo de desarrollo del que por sí solo requiere implementar IParcelable. Por otro lado, el uso de JSON nos permite tener nuestras clases limpias de referencias a elementos de Android además de que es mucho más rápido de implementar. No hay razones para usar ISerialization.

Otro aspecto importante es que realice las pruebas reiniciando la app porque al volver al MainActivity y navegar al SecondActivity con el mismo objeto por segunda vez el tiempo se reducía drásticamente.

Puedes revisar y hacer tus propia comparativa con el código en este enlace, el mismo proyecto contiene los tres escenarios solo es necesario des comentar y comentar los símbolos de compilación en el MainActivity y el SecondActivity según el caso que queramos.





miércoles, 17 de agosto de 2016

Errores "Unzipping failed" al compilar un proyecto Xamarin.Android

Uno de los errores más comunes que ocurren para quien inicia con Xamarin.Android es durante la primera compilación del proyecto. 

Cuando nosotros compilamos por primera vez se descargan de algunas librerías propias de Android, estas librerías se descargan en .zip y luego son descomprimidas durante el proceso de compilación. Estas librerías se almacenan en "%LOCALAPPDATA%\Xamarin\zips"

En este aspecto Visual Studio no es muy explícito acerca de lo que está haciendo mientras compila y dependiendo de nuestra conexión este proceso puede ser muy tardado, en muchas ocasiones esta lentitud causa que cancelemos el proceso pensando en que Visual Studio ya no está haciendo nada, sin embargo, lo que logramos es que las descargas queden a medias y esos archivos se mantienen dañados en la ruta que mencione hace poco.

En la siguiente compilación obtendremos errores como estos:
Error      2             Unzipping failed. Please download https://dl-ssl.google.com/android/repository/android_m2repository_r22.zip and extract it to the C:\Users\User\AppData\Local\Xamarin\Xamarin.Android.Support.Design\23.0.1.3\content directory.                DemoForms.Droid

Error      3             Reason: C:\Users\User\AppData\Local\Xamarin\zips\96659D653BDE0FAEDB818170891F2BB0.zip is not a valid zip file            DemoForms.Droid

Error      4             Please install package: 'Xamarin.Android.Support.Design' available in SDK installer. Java library file C:\Users\User\AppData\Local\Xamarin\Xamarin.Android.Support.Design\23.0.1.3\embedded\classes.jar doesn't exist.     DemoForms.Droid

Para solucionarlo hay que eliminar de manera manual los archivos dañados y recompilar la solución ahora si esperando a que se descargue todo.

Yo cree un archivo bat que ayuda a automatizar un poco esta tarea, el código es el siguiente.

cd %LOCALAPPDATA%\Xamarin\zips\
del /P *.zip


A este código le estoy indicando que solicite confirmación antes de eliminar.


miércoles, 6 de abril de 2016

Haciendo deployment a un dispositivo Android mediante la red TCP IP

En este pequeño tutorial voy a demostrar como se puede hacer la instalación de una app a un dispositivo Android usando la red en lugar del cable USB. Hay varios escenarios donde esto puede ser útil, dos de los casos con los que me he encontrado son los siguientes:
  • Alguna vez tenia que instalar y debuggear una app en un dispositivo hecho a la medida, que tenia Android como Sistema Operativo y del cual no me dieron los driver para el USB y el dispositivo tenia acceso Root y la Terminal funcionando.
  • Actualmente trabajo con una MacBook Pro donde tengo virtualizado Windows con Parallels, esto para poder trabajar con Visual Studio conectado a los Visual Studio Team Services. Para no instalar dentro de la maquina virtual el emulador “Android Player” que provee Xamarin, lo cual no parece una buena idea, lo que hice fue instalar el Android Player en la maquina host y conectarlo a la maquina virtual mediante la red que se crea entre las dos maquinas.
Como lo mencione hace unas líneas para este ejemplo estoy utilizando lo siguiente:
  • Una MacBook Pro con OS X El Capitan
  • Windows 10 virtualizado con Parallels
  • Android Player con una instancia de un Nexus 4 con Lollipop
  • En ambos sistemas tengo Xamarin con todas sus actualizaciones tanto de Xamarin como de XCode en el caso de OS X
En el caso del emulador ya viene configurado con las opciones de desarrollador activadas y listo para el debug mediante TCP. En el caso de un dispositivo físico hay que usar estos comandos antes de realizar la conexión

En un dispositivo con acceso Root desde la terminal usamos los siguientes comandos
su
setprop service.adb.tcp.port 5555
stop adbd
start adbd

En este caso no hablare de un dispositivo sin acceso Root el cual de inicio debe conectarse por USB para habilitar el Debug por TCP.

Ya que tengamos nuestro dispositivo listo continuamos abriendo la consola “Android ABD” desde Visual Studio, podemos hacer la ejecución desde la consola normal solo si tenemos las variable de entorno bien configuradas o si usamos la ruta completa de donde tengamos abd.exe. En el caso de abrir la consola desde Visual Studio esta consola ya esta lista para trabajar sin escribir toda la ruta, para hacerlo presionamos el siguiente icono

image

O podemos acceder desde el menú Tools/Android/Android Adb Command Prompt
En nuestro Android Player presionamos el siguiente botón para obtener la dirección IP asignada al emulador

image
image

Ahora en la consola realizamos la conexión con el siguiente comando
adb connect <Dirección_IP>

image

Si obtenemos las respuesta “connected to” ya se realizo la conexión, tras esto ya podríamos hacer el deployment en el dispositivo conectado como lo hacemos normalmente seleccionando el dispositivo desde la barra de herramienta de VS

image

NOTA: si no aparece el dispositivo hay que intentar reiniciando VS para que lo muestre.

sábado, 28 de noviembre de 2015

Usando SQLite .NET multiplataforma en Xamarin Android, Xamarin IOS, Windows Phone y Windows Store (Parte 3 Android)

En este tercera publicación el turno es para Xamarin Android al igual que la parte anterior no voy a entrar a fondo a los pasos que ya se hicieron anteriormente (Parte 1 y Parte 2).
El primer paso igual que en ocasiones anteriores es crear un proyecto para Xamarin Android
3CreandoDroid
El paso siguiente es enlazar Core mediante el project linker, como lo explique en la primer publicación.
Utilizando SQLite .NET en el proyecto de Android
Con Xamarin Android no hay que agregar referencias extra para hacer funcionar SQLite, por lo cual podemos pasar a desarrollar la interfaz y funcionalidad con solo enlazar Core que contiene los archivos de SQLite .Net.
La interfaz va a contener los mismos campos que las aplicaciones de WP y WS.
image
<?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">
    <TextView
        android:text="Nombre"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/textView4" />
    <EditText
        android:inputType="textPersonName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/edtName" />
    <TextView
        android:text="Edad"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/textView3" />
    <EditText
        android:inputType="number"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/edtAge" />
    <TextView
        android:text="Correo Electrónico"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/textView2" />
    <EditText
        android:inputType="textEmailAddress"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/edtMail" />
    <TextView
        android:text="Teléfono"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/textView1" />
    <EditText
        android:inputType="phone"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/edtPhone" />
    <Button
        android:id="@+id/btnSave"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Guardar" />
    <Button
        android:text="Búsqueda"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/btnSearch" />
</LinearLayout>
El código de la funcionalidad es muy parecido, lo único que es diferente es lo referente a la interacción con la interfaz de usuario.
using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using SQL.Core.Core;
using System.IO;

namespace SQL.Droid
{
    [Activity(Label = "SQL.Droid", MainLauncher = true, Icon = "@drawable/icon")]
    public class MainActivity : Activity
    {
        

        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);

            // Set our view from the "main" layout resource
            SetContentView(Resource.Layout.Main);

            // Get our button from the layout resource,
            // and attach an event to it
            Button btnSave = FindViewById<Button>(Resource.Id.btnSave);
            btnSave.Click += btnSave_Click;
            
            Button btnSearch = FindViewById<Button>(Resource.Id.btnSearch);
            btnSearch.Click += btnSearch_Click;

            InitializateDB();
        }

        void btnSearch_Click(object sender, EventArgs e)
        {
            Intent intent = new Intent(this,typeof(SearchActivity));
            StartActivity(intent);
        }
       
        void btnSave_Click(object sender, EventArgs e)
        {
            var edtName = FindViewById<EditText>(Resource.Id.edtName);
            var edtAge = FindViewById<EditText>(Resource.Id.edtAge);
            var edtPhone = FindViewById<EditText>(Resource.Id.edtPhone);
            var edtMail = FindViewById<EditText>(Resource.Id.edtMail);

            Contact contact = new Contact(edtName.Text,int.Parse(edtAge.Text), edtPhone.Text, edtMail.Text);
            crud.SaveValue<Contact>(contact);

        }

        CRUDManager crud;
        void InitializateDB()
        {
            var sqliteFilename = "ContactsDb";
            var path = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), sqliteFilename); ;

            crud = new CRUDManager(path);

           

        }

    }
}

Para la segunda pantalla la cual muestra los resultados, voy a agregar el código de dos componentes uno es la plantilla xml con la que se mostraran los diferentes campos en la lista de resultados.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
           android:orientation="horizontal"
           android:minWidth="25px"
           android:minHeight="25px"
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:id="@+id/Container"
           android:layout_marginTop="15dp"
           
           android:paddingLeft="20dp">
  <LinearLayout
      android:orientation="vertical"
      android:minWidth="25px"
      android:minHeight="25px"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:id="@+id/linearLayout28">
    <TextView
        android:text="Text"
        android:layout_height="match_parent"
        android:layout_width="wrap_content"
        android:id="@+id/txvName"
        android:textSize="20sp"
        android:textColor="#005DAA" />
    <TextView
        android:text="Text"
        android:layout_height="match_parent"
        android:layout_width="wrap_content"
        android:id="@+id/txvAge" />
    <TextView
      android:text="Text"
      android:layout_height="match_parent"
      android:layout_width="wrap_content"
      android:id="@+id/txvMail" />
    <TextView
        android:text="Text"
        android:layout_height="match_parent"
        android:layout_width="wrap_content"
        android:id="@+id/txvPhone" />
  </LinearLayout>

</LinearLayout>

El otro es la clase “Adapter” que va a funcionar como fuente de datos de la lista que mostrara los resultados.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using SQL.Core.Core;

namespace SQL.Droid
{
    class ContactAdapter : BaseAdapter
    {

        private Activity activity;
        private List<Contact> data;
        private static LayoutInflater inflater = null;


        public ContactAdapter(Activity activity, List<Contact> data)
        {
            this.activity = activity;
            this.data = data;
            inflater = (LayoutInflater)activity.GetSystemService(Context.LayoutInflaterService);

        }

        public Contact GetContact(int position)
        {
            return data[position];
        }


        public override int Count
        {
            get { return data.Count; }
        }

        public override Java.Lang.Object GetItem(int position)
        {
            return position;
        }

        public override long GetItemId(int position)
        {
            return position;
        }

        public override View GetView(int position, View convertView, ViewGroup parent)
        {
            View vi = convertView;
            if (convertView == null)
                vi = inflater.Inflate(Resource.Layout.ContactRow, null);

            TextView name = (TextView)vi.FindViewById(Resource.Id.txvName);
            TextView age = (TextView)vi.FindViewById(Resource.Id.txvAge);
            TextView mail = (TextView)vi.FindViewById(Resource.Id.txvMail);
            TextView phone = (TextView)vi.FindViewById(Resource.Id.txvPhone);
            

            name.Text = data[position].Name;
            age.Text = data[position].Age.ToString();
            mail.Text = data[position].Mail;
            phone.Text = data[position].PhoneNumber;
          

            return vi;
        }
    }
}

Finalmente la pantalla creada es la siguiente:
image
<?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">
    <EditText
        android:inputType="textPersonName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/edtName" />
    <Button
        android:text="Buscar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/btnSearch" />
    <Button
        android:text="Mostrar Todos"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/btnAllItems" />
    <ListView
        android:minWidth="25px"
        android:minHeight="25px"
        android:layout_width="match_parent"
        android:layout_height="350dp"
        android:id="@+id/lvResults" />
    <Button
        android:text="Eliminar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/btnDelete" />
</LinearLayout>
Y su respectiva clase C# para la funcionalidad, al igual que con la primer pantalla la mayoría de los cambios son relativos a la interfaz de usuario
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using SQL.Core.Core;
using System.IO;

namespace SQL.Droid
{
    [Activity(Label = "SearchActivity")]
    public class SearchActivity : Activity
    {
        ListView lvResults;
        Contact selected;
        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);

            SetContentView(Resource.Layout.Search);
            Button btnSearch = FindViewById<Button>(Resource.Id.btnSearch);
            btnSearch.Click += btnSearch_Click;

            Button btnAllItems = FindViewById<Button>(Resource.Id.btnAllItems);
            btnAllItems.Click += btnAllItems_Click;

            Button btnDelete = FindViewById<Button>(Resource.Id.btnDelete);
            btnDelete.Click += btnDelete_Click;

            lvResults = FindViewById<ListView>(Resource.Id.lvResults);
            lvResults.ItemClick += lvResults_ItemClick;

            InitializateDB();
        }

        void lvResults_ItemClick(object sender, AdapterView.ItemClickEventArgs e)
        {
            selected = ((ContactAdapter)lvResults.Adapter).GetContact(e.Position);
        }

        

        void btnDelete_Click(object sender, EventArgs e)
        {
            if (selected != null)
            {
                crud.DeleteValue<Contact>(selected);
                lvResults.Adapter = new ContactAdapter(this, crud.GetAllItems<Contact>());

            }
        }

        


        void btnAllItems_Click(object sender, EventArgs e)
        {

            lvResults.Adapter = new ContactAdapter(this,crud.GetAllItems<Contact>());
        }

        CRUDManager crud;
        void InitializateDB()
        {
            var sqliteFilename = "ContactsDb";
            var path = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), sqliteFilename); ;

            crud = new CRUDManager(path);



        }

        void btnSearch_Click(object sender, EventArgs e)
        {
            var lvResults = FindViewById<ListView>(Resource.Id.lvResults);

            lvResults.Adapter = new ContactAdapter(this,
                new List<Contact>() { crud.GetItem<Contact>(FindViewById<EditText>(Resource.Id.edtName).Text) });
        }
    }
}
Con esto queda concluido el proyecto, al igual que en los casos anteriores dejo el proyecto de Visual Studio http://1drv.ms/1aS1dTL

martes, 17 de noviembre de 2015

Serie de vídeos en español sobre Xamarin en Microsoft Channel 9

El día de hoy se publico el primer vídeo de una serie de vídeos sobre desarrollo con Xamarin, estos vídeos van a abarcar los temas básicos para comenzar a desarrollar apps IOS y Android y lo mejor de todo es que estarán en español.

En este primer vídeo hablo sobre los productos que ofrece Xamarin, cuales son las ventajas de utilizar la plataforma en el desarrollo de apps para IOS y Android; y también muestro como crear una cuenta con licencia de tipo Starter para comenzar a crear proyectos desde Visual Studio. 

Sin mas que decir este es el vídeo: