Si han trabajado con Xamarin.Forms habrán notado que nuestro control más usado , el control Entry , que usamos como caja de texto luce muy distinto en iOS y en Android.

Ahora, en nuestros diseños muchas veces necesitamos personalizarlos y hacer que luzcan iguales para lograr un diseño como este:

Así que me tomé la tarea de extender nuestro control Entry para que nos permita personalizar algunas cosas como el BorderColor, el Padding y el CornerRadius .

Si desean pueden revisar el video al respecto.

TLDR;

Si quieres revisar las personalizaciones e incluirlas en tu proyecto, puedes revisar el código directamente en GitHub https://github.com/jesulink2514/XamBooksApp/tree/feature/feat-entry

Exploremos las personalizaciones

Para realizar este control he utilizado mi aplicación de ejemplo XamBooksApp que se encuentra en GitHub disponible para que ustedes puedan revisar el código.

He creado la clase StandardEntry, esta clase extiende el control del Xamarin.Forms y le agregado unas propiedades adicionales.

Como pueden ver en este caso no hay gran cosa en nuestra clase en nuestro proyecto pues lo importante sucede en cada proyecto de plataforma. En nuestro caso los Custom Renderers.

Empecemos por Android

Aquí tengo mi renderer que está exportado apropiadamente para sobrescribir y enlazarse con el control StandarEntry.

Obviamente yo no quiero perder la funcionalidad que ya ha implementado el equipo de Xamarin.Forms, sólo quiero extenderla por lo tanto estoy heredando de la clase  EntryRenderer que se usa por defecto para nuestro control Entry en Xamarin.Forms.

Para que funcione en Android necesito definir el constructor que pueda recibir el contexto de Android.

Lo que estoy haciendo es sobrescribir el método CreateNativeControl, no porque vaya a reemplazarlo por otro control nativo, voy a seguir utilizando en EditText pero lo que quiero hacer es actualizar el Background de nuestro control en base a las propiedades que están definiendo momento de crearlo.

Para eso vamos a utilizar este UpdateBackground que es nuestro método donde sucede la respuesta a las personalizaciones.

En nuestro caso como hemos extendido la clase EntryRenderer la propiedad Element, que es una propiedad estructural en un render (pueden ver mas sobre las propiedades básicas de un Renderer aqui ) es de tipo Entry. Esta propiedad es la que me permite tener acceso al a la instancia de la clase de Xamarin.Forms, es decir a nuestra abstracción. Nosotros necesitamos acceder a nuestra propia clase, mi StandardEntry y dado que no puedo sobrescribir la propiedad Element lo que hago es crear una nueva propiedad que hace el casting ,solamente para facilitarme el acceso a las propiedades nuevas que yo he definido.

Notarán que también es sobre escrito el método OnElementPropertyChanged esto me va a permitir reaccionar al cambio en las propiedades en el control. En este caso ya sea que cambie mi CornerRadius, BorderThickness, BorderColor,etc , yo siempre voy a actualizar mi Background. No olviden llamar a la clase base para conservar la funcionalidad ya implementada en el control Entry.

Veamos este código de Android que utilizo para aplicar las personalizaciones que deseo.

Utilizo un GradientDrawable como fondo para mi control, este soporta lo que necesito, el fondo, el borde, etc.  Aquí resaltan los siguientes métodos de extensión que el equipo de Xamarin.Forms ha puesto a nuestra disposición cuando interactuamos con la plataforma.

  • ToAndroid, este método de extensión aplica sobre la clase Color de Xamarin.Forms y lo transforma en la clase Color que necesita Android.
  • ToPixels, este método de extensión convierte la unidad que utilizamos en Xamarin.Forms que es independiente de la densidad de la pantalla a pixeles que son lo que usamos en Android.

Finalmente voy a tomar mi propiedad Padding que es un Thicknes de Xamarin.Forms y obtener cada componente. Igual que a los valores anteriores lo transformamos a píxeles.

También debemos sobrescribir el método llamado UpdateBackgroundColor que ya es utilizado por la clase base cuando alguien fijaba la propiedad BackgroundColor, recuerdan que eso es una propiedad normal de nuestro control

Es tiempo de echarle un ojo a iOS

Nuestro renderer para iOS está apropiadamente registrado para ser utilizado con mi StandardEntry.

Vemos la primera diferencia y es que no deriva de EntryRenderer sino que EntryRendererBase, esto es gracias a que si revisamos el código fuente de Xamarin.Forms que es open source (no hay mejor fuente para poder revisar los Renderers) el equipo de Xamarin ha creado la clase genérica para poder reemplazar el control nativo por uno que derive de UITextField.

Y ¿Por qué necesitaría crear una clase derivada de UITextField? Pues eso mismo me pregunte yo hasta que trate de agregarle padding a mi control en iOS.

Para hacerlo necesito sobrescribir los métodos TextRect, PlaceholderRect y EditingRect para determinar el tamaño del texto a mostrar, del placeholder y del cuadro de edición. Esto se realiza utilizando la característica de UIEdgeInsets, que es lo mas cercano al concepto de Padding en iOS.

De forma similar a Android, creamos la propiedad ElementV2, para acceder a las nuevas propiedades definidas en nuestro control derivado.

Tenemos un constructor por defecto donde definimos el rectángulo de dimensiones por defecto para el control.

Sobrescribimos el método CreateNativeControl para poder definir las propiedades del nuevo control ( UITextFieldPadding) y actualizar su background.

Pueden notar la similitud con Android solo que aquí en iOS el fondo lo definimos usando la propiedad Layer.

En el caso de iOS el único método de extensión que requerimos es el ToCGColor, que transforma un color de Xamarin.Forms en un CGColor, que es lo que requiere iOS.

Y voilá ...

https://github.com/jesulink2514/XamBooksApp/tree/feature/feat-entry