Modificar el comportamiento de un widget Android

Vamos a modificar el comportamiento estándar de un widget Android para adaptarlo a nuestras necesidades. Queremos usar un DateTime Picker que viene en la librería por defecto pero nos interesa que cuando se incremente o decremente un campo (día, mes o año) el resto también se vean afectados: cuando el día del mes supere el 30 (o 31) el contador del mes debe saltar al siguiente, y lo mismo debe aplicarse al incrementar el mes para modificar el año. Con este requisito ya observamos una primera dificultad: los límites de cada mes varían entre 30 y 31 y tenemos además Febrero que puede caer en año bisiesto o no. Para evitar complicar el código usaremos la clase DateTime de C# que nos permite incrementar/decrementar tranquilamente cada campo dejando en manos de la función normalize() el cálculo correcto de la fecha. Por ejemplo, podemos crear
DateTime Fecha = new DateTime(año, mes, dia);
donde 0 <= mes <= 11 y 1 <= dia <= 31. Si creamos una Fecha tal como 30/11/2011 e incrementamos el campo día obtendremos una fecha ilegal (31/11/2011). Aplicando la función normalize() haremos que se recalcule correctamente:
Fecha.normalize(false);   // devuelve 1/12/2011
donde el valor booleano indica a la función si debe tener en cuenta el horario de verano o no (false indica que si, véase la especificación). Ya tenemos pues una manera fácil de calcular el valor de cada campo cuando el usuario haga click en un botón del widget para incrementar o decrementar la fecha. El siguiente problema que debemos resolver es cómo acceder a dichos botones para sustituir su evento click por el nuestro. La solución está en la propiedad Touchables de las vistas (View). Esta propiedad nos devuelve una colección (IList) de todas las vistas descendientes de la actual. En nuestro caso si leemos Touchables de un DateTime Picker obtendremos una lista de los botones y campos de texto gestionados por el widget, lo que quedaría como:
DatePicker Data = (DatePicker)FindViewById(Resource.Id.data);
IList<View> Vistes = Data.Touchables;
donde Resource.Id.data es el recurso DateTime Picker que estamos usando. Tenemos pues la lista de componentes que conforman el widget, el índice que ocupan en la lista es:
Sabiendo ésto ya podemos acceder a los botones para modificar su comportamiento. Extenderemos la clase con la que cargamos el widget con la interface IONClickListener para implementar un evento OnClick que es el que sustituirá al original.  También necesitamos implementar la interface DatePicker.IOnDateChangedListener para pasarle nuestra clase al método de inicialización del DatePicker (Init()). El constructor quedará pues como:
public class ModifDateTimePicker : DatePicker.IOnDateChangedListener, View.IOnClickListener
{
   DatePicker Data;
   Time Ahora = new Time(Time.CurrentTimezone);
   IList<view> Vistas;

   protected override void OnCreate(Bundle bundle)
   {
       base.OnCreate(bundle);
       Data = (DatePicker)FindViewById(Resource.Id.data);
       Data.DescendantFocusability = Android.Views.DescendantFocusability.BlockDescendants;  // Para bloquear modificación campos de texto
       //  Obtenemos la lista de componentes del widget y cambiamos los eventos OnClick de los botones
       Vistas = Data.Touchables;
       ((View)Vistas[0]).Tag = 0;  //  Botones +
       ((View)Vistas[0]).SetOnClickListener(this); //  +Dia
       ((View)Vistas[3]).Tag = 3;
       ((View)Vistas[3]).SetOnClickListener(this); //  +Mes
       ((View)Vistas[6]).Tag = 6;
       ((View)Vistas[6]).SetOnClickListener(this); //  +Año
       ((View)Vistas[2]).Tag = 2;  //  Botones -
       ((View)Vistas[2]).SetOnClickListener(this); //  -Dia
       ((View)Vistas[5]).Tag = 5;
       ((View)Vistas[5]).SetOnClickListener(this); //  -Mes
       ((View)Vistas[8]).Tag = 8;
       ((View)Vistas[8]).SetOnClickListener(this); //  -Año
       Ahora.SetToNow();
       Data.Init(Ahora.Year, Ahora.Month, Ahora.MonthDay, this);
   }
}
El método OnCreate simplemente carga el widget, lee la lista de componentes y modifica el evento OnClick de los que nos interesan, que son los botones +/-. A continuación toma la fecha actual e inicializa el DateTime Picker. Se observa que además de modificar el ClickListener usamos la propiedad Tag para guardar el índice del componente modificado. Esto nos servirá en la función OnClick para saber qué acción debemos tomar. Dicha función puede implementarse como:
void View.IOnClickListener.OnClick(View view)
{
   switch ((int)view.Tag)
   {
       case 0: Ahora.MonthDay++; break;
       case 2: Ahora.MonthDay--; break;
       case 3: Ahora.Month++; break;
       case 5: Ahora.Month--; break;
       case 6: Ahora.Year++; break;
       case 8: Ahora.Year--; break;
   }
   Ahora.Normalize(false);
   Data.UpdateDate(Ahora.Year, Ahora.Month, Ahora.MonthDay);
}
Cada vez que se dispara el evento se comprueba el Tag de la vista que lo solicita y así sabemos a qué botón se corresponde y la acción que debemos tomar. A continuación se normaliza la fecha como vimos antes y se actualiza el widget. Para terminar implementamos el listener IOnDateChangedListener a que nos obliga la interface pero lo dejamos vacío ya que no lo necesitamos.
void DatePicker.IOnDateChangedListener.OnDateChanged(DatePicker dp, int any, int mes, int dia) { }
Simple y sencillo.

No hay comentarios:

Publicar un comentario