Agregue restricciones de alcance a las funciones de Kotlin (tome Compose como ejemplo)

prefacio

Me pregunto si ya has empezado a entender Jetpack Compose.

Si ya has empezado a entender y lo has escrito a mano. Bueno, no sé si te has dado cuenta de que hay muchas aplicaciones de Scopes en Compose. Por ejemplo, weightlos modificadores solo se pueden usar en RowScopeo ColumnScopeámbitos. Como otro ejemplo, itemlos componentes solo se pueden usar en LazyListScopeel alcance.

Si no ha aprendido acerca de Compose, también debe saber que hay 5 funciones de alcance en la biblioteca estándar de Kotlin: let() apply() also() with() run()estas 5 funciones retendrán y devolverán el objeto de contexto de diferentes maneras, es decir, cuando se llame a estas funciones, en El código escrito en sus parámetros lambda estará en un ámbito específico.

No sé si has pensado en cómo se implementan estas restricciones de alcance. Si queremos personalizar una función Composable que solo admite el uso en un ámbito específico, ¿cómo deberíamos escribirla?

Este artículo te resolverá esta duda.

alcance

Pero antes de comenzar oficialmente, agreguemos algunos conocimientos básicos sobre el alcance en Kotlin.

que es alcance

De hecho, para nosotros los programadores, sin importar el idioma que aprendamos, debemos tener una comprensión del alcance.

Para dar un ejemplo simple:

val valueFile = "file"

fun a() {
    
    
    val valueA = "a"
    println(valueFile)
    println(valueA)
    println(valueB)
}

fun b() {
    
    
    val valueB = "b"
    println(valueFile)
    println(valueA)
    println(valueB)
}

No es necesario ejecutar este código para saber que definitivamente informará un error, porque no se puede acceder en la función a valueB, no se puede acceder en la función b valueA. Pero se puede acceder a ambas funciones con éxito valueFile.

Esto se debe a que valueFileel alcance de es todo el archivo .kt, es decir, siempre que esté en el código de este archivo, se puede acceder a él.

valueALos ámbitos de y están valueBrespectivamente en función a y b, obviamente solo se pueden usar en sus respectivos ámbitos.

De manera similar, si queremos llamar a un método o función de una clase, también debemos considerar el alcance:

class Test {
    
    
    val valueTest = "test"

    fun a(): String {
    
    
        val valueA = "a"
        println(valueTest)
        println(valueA)

        return "returnA"
    }
    
    fun b() {
    
    
       println(valueA)
       println(valueTest)
       println(a())
    }
}

fun main() {
    
    
    println(valueTest)
    println(valueA)
    println(a())
}

Los ejemplos dados aquí pueden no ser apropiados, pero aquí es para ilustrar esta situación, no se enrede demasiado ~

Obviamente, el código anterior mainno puede acceder a las variables valueTesty en la función valueA , y no puede llamar a la función a(); mientras que Testla función en la clase a()obviamente puede acceder valueTesta y valueA , y la función b()también puede llamar a la función a(), que puede acceder a la variable valueTestpero no puede acceder a la variable valueA .

Esto se debe a que las funciones a()y b()la variable valueTestestán en el mismo ámbito, Testel ámbito de la clase.

La variable valueAse encuentra a()en el ámbito de la función, y dado que a()se encuentra Testen el ámbito de , de hecho valueAel ámbito de aquí se denomina ámbito anidado, es decir, se encuentra en el ámbito de a()y al mismo tiempo.Test

Debido a que esta sección es solo para presentar lo que vamos a presentar hoy, se presenta brevemente mucho conocimiento sobre el alcance. Para obtener más conocimiento sobre el alcance, lea la Referencia 1.

Funciones con alcance en la biblioteca estándar de Kotlin

En el prefacio dijimos que hay cinco cosas llamadas funciones de alcance en la biblioteca estándar de Kotlin: with, run, let, also, apply.

¿Qué hacen?

Primero veamos un formulario de código que encontramos a menudo:

val person = Person()
person.fullName = "equationl"
person.lastName = "l"
person.firstName = "equation"
person.age = 24
person.gender = "man"

En algunos casos, es posible que necesitemos escribir un montón de repeticiones muchas veces person, lo cual es muy legible y engorroso de escribir.

En este punto podemos usar la función scope, por ejemplo usando withpara reescribir:

with(person) {
    
    
    fullName = "equationl"
    lastName = "l"
    firstName = "equation"
    age = 24
    gender = "man"
}

En este punto, podemos omitirlo persony acceder directamente o modificar su valor de atributo, esto se debe a que withel primer parámetro recibe el objeto de contexto lambda que debe usarse como segundo parámetro, es decir, el segundo parámetro lambda es anónimo El alcance de la función es el objeto pasado por el primer parámetro. En este momento, el indicador del IDE también señaló que el alcance de la función anónima de with es Person:

1.png

Entonces, en esta función anónima, puede acceder directamente o modificar los atributos de Persona.

De manera similar, también podemos usar runla función para reescribir:

person.run {
    
    
    fullName = "equationl"
    lastName = "l"
    firstName = "equation"
    age = 24
    gender = "man"
}

Se puede ver que runes withmuy similar a , excepto que runrecibe el objeto de contexto en forma de función de extensión , y su parámetro es solo una función anónima lambda.

También hay let:

person.let {
    
    
    it.fullName = "equationl"
    it.lastName = "l"
    it.firstName = "equation"
    it.age = 24
    it.gender = "man"
}

La diferencia entre it y runes que el objeto de contexto en la función anónima ya no es un receptor implícito (this), sino que existe como un parámetro (it).

Usa also()entonces:

person.also {
    
    
    it.fullName = "equationl"
    it.lastName = "l"
    it.firstName = "equation"
    it.age = 24
    it.gender = "man"
}

Al igual que let, también es una función de extensión, y el contexto también se pasa a la función anónima como parámetro, pero de manera diferente, letdevolverá el objeto de contexto, lo que puede facilitar llamadas en cadena, como:

val personString = person
    .also {
    
    
        it.age = 25
    }
    .toString()

Y finalmente apply:

person.apply {
    
    
    fullName = "equationl"
    lastName = "l"
    firstName = "equation"
    age = 24
    gender = "man"
}

Al igual que also, es una función de extensión y también devuelve un objeto de contexto, pero su contexto será el receptor implícito en lugar de un parámetro de la función anónima.

El siguiente es un cuadro comparativo y una tabla de sus 5 funciones:

2.png

función forma contextual valor devuelto ¿Es una función de extensión?
con receptor implícito (este) función lambda (Unidad) No
correr receptor implícito (este) función lambda (Unidad)
dejar El parámetro de la función anónima (it) función lambda (Unidad)
también El parámetro de la función anónima (it) objeto de contexto
aplicar receptor implícito (este) objeto de contexto

Restricciones de alcance en Compose

Como dijimos en el prefacio, hay muchas aplicaciones de restricciones de alcance en Compose.

Por ejemplo, el modificador Modifier, de esta lista de modificadores Compose , también podemos ver que el alcance de muchos modificadores es limitado:

3.png

La razón para restringir los modificadores aquí es muy simple:

En el sistema Android View, no hay ningún tipo de seguridad. Los desarrolladores generalmente se encuentran probando diferentes parámetros de diseño para descubrir cuáles se consideran y su significado en el contexto de un padre en particular.

En el sistema de vista xml tradicional, no hay restricciones en los parámetros del diseño, lo que lleva al hecho de que todos los parámetros se pueden usar en cualquier diseño, lo que causará algunos problemas. Ligeramente, los parámetros no son válidos y se escriben un montón de parámetros inútiles; en serio, puede interferir con el uso normal del diseño.

Por supuesto, la restricción del modificador Modifier es solo una de las aplicaciones en Compose, y hay muchos ejemplos de restricciones de alcance en Compose, por ejemplo:

4.png

En la figura anterior itemsolo se puede usar en LazyListScopeel alcance, drawRectsolo se puede usar en DrawScopeel alcance.

Por supuesto, como dijimos antes, no solo hay funciones y métodos en el alcance, sino también las propiedades de la clase, por ejemplo, una propiedad DrawScopellamada se proporciona en el alcance size, y el tamaño actual del lienzo se puede obtener a través de él:

5.png

Entonces, ¿cómo se logran estos?

Personaliza nuestra función de límite de alcance

principio

Antes de comenzar a implementar nuestra propia función de alcance, primero debemos comprender el principio.

CanvasAquí tomamos el de Compose como ejemplo.

Primero Canvasla definición de:

6.png

Se puede ver que aquí Canvasse reciben dos parámetros : el modificador y la lambda de onDraw, y el Receiver (receptor) de esta lambda es DrawScope, es decir, el alcance de la función anónima onDraw es limitado DrawScope, lo que también significa que se puede usar en funciones anónimas usar DrawScopepropiedades, métodos, etc. internamente dentro del alcance.

Veamos DrawScopequé divino es esto:

7.png

Puede ver que esta es una interfaz, que define algunas variables de atributos (como dijimos anteriormente size) y algunos métodos (como dijimos anteriormente drawRect).

Luego implemente esta interfaz y escriba el código de implementación específico:

8.png

lograr

Entonces, en resumen, si queremos implementar nuestras propias restricciones de alcance, se puede dividir aproximadamente en tres pasos:

  1. Escritura de interfaces como ámbitos
  2. implementar esta interfaz
  3. En el método expuesto, el receptor de parámetros lambda utiliza la interfaz definida anteriormente

Tomemos un ejemplo.

Supongamos que queremos implementar una capa de guía de máscara en Compose para guiar a los nuevos usuarios a operar, algo como esto:

principal_intro.gif

Fuente de la imagen Intro-showcase-view

Pero esperamos que las indicaciones en la capa de guía se puedan diversificar, por ejemplo, puede admitir indicaciones de texto, indicaciones de imagen e incluso reproducir indicaciones de video o animación, pero no queremos que estos elementos de indicación se llamen fuera de la capa de máscara. , debido a que dependen de algunos parámetros de la capa de máscara, provocarán errores si se llaman externamente.

En este momento, el uso de restricciones de alcance es muy apropiado.

Primero, escribimos una interfaz:

interface ShowcaseScreenScope {
    
    
    val isShowOnce: Boolean

    @Composable
    fun ShowcaseTextItem()
}

En esta interfaz, definimos una variable de atributo isShowOncepara indicar si la capa de guía solo se muestra una vez y definimos un método ShowcaseTextItempara mostrar una cadena de texto en la capa de guía. De manera similar, también podemos definir para ShowcaseImageItemindicar imágenes de visualización.

Luego implemente esta interfaz:

private class ShowcaseScopeImpl: ShowcaseScreenScope {
    
    

    override val isShowOnce: Boolean
        get() = TODO("在这里编写是否只显示一次的逻辑")

    @Composable
    override fun ShowcaseTextItem() {
    
    
        // 在这里写你的实现代码
        Text(text = "我是说明文字")
    }
}

En la implementación de la interfaz, escriba el código lógico de implementación correspondiente según nuestras necesidades.

Finalmente, escriba un Composable que esté disponible para llamadas externas:

@Composable
fun ShowcaseScreen(content: @Composable ShowcaseScreenScope.() -> Unit) {
    
    
    // 在这里实现其他逻辑(例如显示遮罩)后调用 content
    // ……
    ShowcaseScopeImpl().content()
}

En este componible, podemos procesar otra lógica primero, como mostrar la interfaz de usuario de la capa de máscara o mostrar animaciones, y luego llamar para ShowcaseScopeImpl().content()combinar los subelementos que pasamos.

Finalmente, cuando use solo llame:

ShowcaseScreen {
    
    
    if (!isShowOnce) {
    
    
        ShowcaseTextItem()
    }
}

Por supuesto, esto ShowcaseTextItem()y isShowOnceestán en ShowcaseScreenScopeel alcance y no se pueden llamar fuera:

9.png

Resumir

Este artículo presenta brevemente el concepto de alcance en Kotlin y la función de alcance en la biblioteca estándar, y se extiende a la aplicación de alcance en Compose, finalmente analiza el principio de implementación y explica cómo personalizar una función de alcance de Compose propia.

La redacción de este artículo puede ser relativamente simple, y muchos puntos de conocimiento son precisos, sin demasiada explicación. Se recomienda que los lectores lean los artículos escritos por otros peces gordos en el enlace de referencia al final del artículo.

Referencias

  1. Ámbitos y funciones de ámbito
  2. Kotlin DSL en acción: escribe código como Compose
  3. Ámbito de componentes componibles a un componente componible principal
  4. Modificadores de Compose: escribe seguridad en Compose

Supongo que te gusta

Origin blog.csdn.net/sinat_17133389/article/details/130894365
Recomendado
Clasificación