[Xojo 2024r3] Android: Mejoras en Declare y Library

A continuación encontrarás traducido al castellano el artículo escrito por Paul Lefebvre y publicado originalmente en el Blog Oficial de Xojo.

En Xojo 2024r3 encontrarás múltiples mejoras en el soporte de Declare y la Librería de Kotlin que, sumadas a las anteriores mejoras, deberían permitirte utilizar más del ecosistema nativo en tus proyectos Android.

Este artículo describe las cosas que puedes hacer con los Declare y las Librerías en los proyectos Android creados con Xojo.

Tipos Entero en Declare

El tamaño específico de los diferentes tipos de Entero es ahora más preciso cuando se utiliza en los Declare, puedes mapearlos tal y como se muestra:

Tipo de XojoTipo de Kotlin
Int8, ByteByte
UInt8UByte
Int16Short
UInt16UShort
Int32Int
UInt32UInt
Integer, Int64Long
UInteger, UInt64ULong

Cuando se mapean con las funciones de la API Android o con las funciones de la Librería, asegúrate de utilizar el tipo de Xojo correcto que se corresponda con lo esperado en el lado de Android. Lo más probable es que utilices Int32 e Integer.

Si ya habías creado Declares previamente, probablemente tendrás que cambiar el uso de Integer a Int32 como el tipo mapeado debido a este cambio y comparado con las anteriores versiones.

Tipos Nullables

Las variables de objeto en Xojo (así como las propiedades, parámetros y los valores devueltos) pueden ser Nil. En Kotlin, el comportamiento por omisión es que estos no pueden ser null (el equivalente a Nil). Para asegurar que no obtengas errores de compilación con tus parámetros, asegúrate de declararlos como nullable en Kotlin añadiendo el caracter “?” al final del nombre del tipo.

Contexto de la Aplicación

Muchas de las API de Android requieren de un contexto de aplicación. Esto no está disponible en una Librería, por lo que ha de proporcionarse por la app. Hay una propiedad para ello:

MobileApplication.AndroidContextHandle As Ptr

Puedes pasar este valor a la función de la Librería que, en consecuencia, puede guardarlo o usarlo directamente.

Uso de Ptr

Xojo utiliza el tipo Ptr para conectar con los Declare, sin embargo Kotlin no dispone del tipo Ptr. En vez de ello utiliza el tipo Any. Esto significa que si estás pasando cualquier cosa que es un Ptr a Kotlin (como pueda ser el contexto de la app, indicado anteriormente), la función declarada de tu librería debería utilizar “Any?” como el tipo para el parámetro.

En tu función, haz un cast del parámetro al tipo en concreto que quieras. Esta misma regla se aplica a los valores devueltos. Si el valor devuelto en un Declare es un Ptr, entonces la declaración en la función de Kotlin debería de ser “Any?”.

También puedes utilizar Ptr con las llamadas de la API para guardar las referencias a objetos tal y como se describe a continuación.

Permisos de Librería

En función de la API que estés usando, es posible que necesites añadir permisos al archivo manifest.xml de la app para la librería. Estos permisos también han de aplicarse a la app principal. Esto puede hacerse desde la pestaña Avanzado en Android Build Settings. Aquí verás la propiedad Permissions en el Inspector, donde puedes añadir un permiso por línea.

Las versiones anteriores te permiten separar los permisos mediante espacios o comas. De aquí en adelante, cada permiso debe indicarse en su propia línea. Las líneas vacías se ignoran.

Aquí puedes añadir las constantes de permisos requeridas por tu Librería. Además, puedes continuar la constante con atributos adicionales y estos también se aplicarán en el archivo de manifiesto de la aplicación.

Por ejemplo, para incluir el atributo android:maxSdkVersion con el permiso BLUETOOTH:

android.permission.BLUETOOTH android:maxSdkVersion="30"

Dependencias de Librería

De forma similar a lo que ocurre con los permisos, puede que tu Librería haga un uso de dependencias adicionales, las cuales podrían ser incluso otras Librerías. Estas se añaden en el archivo build.gradle para la Librería, pero también han de incluirse en la app principal.

También puedes añadir estas dependencias utilizando la propiedad Dependencies en la pestaña Advanced de Android Build Settings.

Creación de Objetos

Las anteriores versiones de Xojo sólo te permitían llamar métodos Companion (en esencia, métodos compartidos) correspondientes a las clases de Librería. Ahora también pueden funcionar sobre las instancias de clase y llamar a los métodos de instancia. Esto de proporciona más control y la capacidad de conectar con las API de Android.

Para crear un objeto, la clase de tu Librería debe tener un método de factoría en un Companion que devuelva una nueva instancia. Es más claro utilizar Create() como nombre para dicho método.

En Xojo puedes crear un Declare sobre este método create() e invocarlo, guardando el resultado en un Ptr.

Declare Function create Lib "com.example.utility.counter" () As Ptr
Var counter As Ptr = create

En vez de utilizar un método compartido de factoría también puedes llamar al constructor de la clase utilizando esta sintaxis:

Declare Function counter Lib "com.example.utility.counter" () As Ptr

Dado que el nombre de función tiene el mismo nombre que la clase este llamará al constructor de la clase.

Cuando quieras llamar a un método sobre dicha instancia, pasa la instancia como un Ptr. En primer lugar, haz un Declare del método añadiendo “.instance” a la ubicación de la Librería para indicar que estás llamando a una instancia de la clase.

Declare Function increment Lib "com.example.utility.counter.instance" (ref As Ptr) As Integer

Luego, puedes llamar al método y guardar su valor.

Var count As Integer = increment(counter)

La referencia como Ptr debe ser el primer parámetro pasado en un Declare. Luego, añade el resto de parámetros tal y como es habitual.

También puedes llamara a los métodos de instancia directamente en una API de Android de esta forma.

Callbacks de Método

Algunas API de Android utilizan callbacks a los métodos proporcionados. Dichos métodos en Xojo son Delegates y son los métodos que proporcionas utilizando AddressOf. Es importante que la signatura de dicho método se corresponda exactamente con lo esperado por el callback, lo que significa que estarás limitado al uso de tipos Boolean y Ptr.

Si quieres utilizar una API que emplee sus propios callback, entonces la mejor alternativa sería crear una Librería y dejar que la API realice el callback sobre los métodos de tu propia Librería, la cual puede llamar posteriormente a tus métodos de Xojo.

Puedes pasar la referencia al método de Xojo utilizando AddressOf así:

Var cb As Ptr = AddressOf TestCallback
Declare Sub xojocallback Lib "com.example.utility.callback" (cb As Ptr)
xojocallback(cb)

El método de Xojo sólo puede utilizar parámetros de tipo Boolean y Ptr. Sería así:

Public Sub TestCallback(b As Boolean, i As Ptr, s As Ptr)

En la librería de Kotlin has de hacer un cast sobre el parámetro entrante a la referencia de la función. Este es un proceso en dos pasos donde verificas que la función tiene la cantidad correcta de parámetros y entonces le haces un cast a los tipos de parámetro específicos. Este sería un ejemplo de código:

if (cb is Function3<*, *, *, *>) {
    try {
         (cb as Function3<boolean, long,="" string,="" unit="">).invoke(true, 42, "Hello")
         println("Sent callback to Xojo")
    } catch (e: ClassCastException) {
         println("Casting failed: ${e.message}")
    }
}</boolean,>

Aquí, “Function3” indica una función con 3 parámetros (el último “*” indica el tipo devuleto), pero puedes cambiarlo según necesites utilizando Function1, Function2, etc. Puedes pasar cualquier cosa de vuelta utilizando el tipo Ptr, pero el código Xojo ha de convertilo utilizando los métodos de Ptr, de modo que no deberías de cambiar los tipos de los tipos devueltos para que no sean distintos de lo que espera el código de Xojo.

Declares sobre Objetos

Los Declare sobre objetos no son algo nuevo, pero se describen aquí por completitud. En esencia un Declare de Objeto utiliza una sintaxis especial para declarar un control de la IU.

La forma típica de utilizar un Declare sobre un objeto es añadiendo un método Extends a un Módulo. Por ejemplo, un método de extensión para permitir que MobileButton cambie sus colores podría ser como sigue:

SetBackColor(Extends ctrl As MobileButton, c As Color)

El Declare del Objeto sería como sigue:

Declare Sub setBackgroundColor Lib "Object:ctrl:MobileButton" (myColor As Int32)
setBackgroundColor(c.ToInteger)

En este caso estamos llamando a la función setBackgroundColor. El componente Lib indica este nuevo tipo de declare de Objeto: un Objetos, separado por dos puntos, el nombre Xojo del objeto (en este caso se trata de nuestro parámetro “ctrl”), seguido por otros dos puntos y el tipo de Xojo para el objeto.

Declares Kotlin

Puedes utilizar la designación específica de Android en “Kotlin” para indicar que lo especificado en la sección Alias del Declare debería de utilizarse tal cual. Esto resulta particularmente útil cuando quieres hacer un cast de los valores Ptr a un tipo concreto para su uso con las API del sistema operativo.

Este código obtiene el objeto ColorStateList para un valor de Color en Xojo:

Var c As Color = Color.Blue
 
Declare Function valueOf Lib "android.content.res.ColorStateList:Kotlin" Alias _
"android.content.res.ColorStateList.valueOf(android.graphics.Color.argb(alpha.toInt(), r.toInt(), g.toInt(), b.toInt()))" _
(alpha As Int32, r As Int32, g As Int32, b As Int32) As Ptr
 
Var csl As Ptr = valueOf(255 - c.Alpha, c.Red, c.Green, c.Blue)

Luego se puede utilizar este código para llamar a setStrokeColor en un MobileButton:

Declare Sub import Lib "android.content.res.ColorStateList:Import" // Importa la clase ColorStateList
 
Declare Sub setStrokeColor Lib "Object:ctrl:MobileDateTimePicker:Kotlin" Alias "setStrokeColor(strokecolor as ColorStateList)" (strokeColor As Ptr)
setStrokeColor(csl)

Como puedes ver en el Alias es la llamada al método Kotlin el encargado de realizar un cast del valor Ptr entrante a un ColorStateList.

Import

Observa el declare “import” en el anterior fragmento de código. Este también es específico de Android. Este Declare indica que debería de importarse una clase concreat de modo que pueda utilizarse para el cast. Si no incluyes ese import, entonces el cast de ColorStateList provocaría un error de compilación debido a que ColorStateList no sería conocido.

Sólo necesitas importar una vez una clase específica. Una vez que lo hayas hecho, podrá ser referenciada por cualquier otro Declare, incluso aquellos que se encuentren en métodos diferentes.

El uso de un import en un Declare es opcional, y tiene como objetivo simplificar la llamada al método Kotlin en el Alias. En vez de usar import puedes indicar la ruta completa de la clase en el cast, tal y como se muestra aquí:

Declare Sub setStrokeColor Lib "Object:ctrl:MobileDateTimePicker:Kotlin" Alias "setStrokeColor(strokecolor as android.content.res.ColorStateList)" (strokeColor As Ptr)
setStrokeColor(csl)

Handles

Muchas de las clases en el framework cuentan ahora con propiedades Handle (o métodos) que puedes utilizar con las diferentes técnicas de Declare descritas. Entre otras: FolderItem, Font, GraphicsPath, Locale, TimeZone, Graphics, Picture. Además, los controles mobile tienen propiedades Handle que pueden servir como una alternativa para los Declare de Objetos.

Proyecto de Ejemplo

Puedes descargar el proyecto de ejemplo que muestra una librería y algunos de los conceptos descritos en este artículo. Puedes utilizar dicho proyecto como plantilla o bien punto de partida para crear tus propias librerías o Declares.

Proyecto de Librería de ejemplo.

También puedes echar un vistazo al proyecto de código abierto Android Design Extensions, el cual contiene cientos de Declares que pueden mejorar tus apps Android.

referencias

Android Libraries blog post
Android Declares blog post

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *