Fermer

décembre 13, 2022

Mise en surbrillance de la saisie de texte avec Jetpack Compose

Mise en surbrillance de la saisie de texte avec Jetpack Compose


Nous avons récemment lancé une nouvelle fonctionnalité chez Buffer, appelée Idées. Avec Ideas, vous pouvez stocker toutes vos meilleures idées, les modifier jusqu’à ce qu’elles soient prêtes et les déposer directement dans votre file d’attente Buffer. Maintenant que Ideas a été lancé dans nos applications Web et mobiles, nous avons le temps de partager quelques enseignements tirés du développement de cette fonctionnalité. Dans cet article de blog, nous expliquerons comment nous avons ajouté la prise en charge de la mise en surbrillance d’URL à Ideas Composer sur Android, à l’aide de Jetpack Compose.


Nous avons commencé à adopter Jetpack Compose dans notre application en 2021 – en l’utilisant en standard pour créer toutes nos nouvelles fonctionnalités, tout en l’adoptant progressivement dans les parties existantes de notre application. Nous avons construit l’ensemble de la fonctionnalité Ideas à l’aide de Jetpack Compose. Ainsi, parallèlement à un développement plus rapide des fonctionnalités et à une plus grande prévisibilité dans l’état de notre interface utilisateur, nous avons eu de nombreuses opportunités d’explorer davantage Compose et d’en savoir plus sur la manière de répondre à certaines exigences dans notre application.

Dans le compositeur d’idées, nous prenons en charge la mise en évidence des liens dynamiques. Cela signifie que si vous tapez une URL dans la zone de texte, le lien sera mis en surbrillance – en appuyant sur ce lien, une fenêtre contextuelle « Ouvrir le lien » s’affichera, qui lancera le lien dans le navigateur lorsque vous cliquerez dessus.

Dans cet article de blog, nous allons nous concentrer sur le lien mettant en évidence l’implémentation et comment cela peut être réalisé dans Jetpack Compose en utilisant le TextField composable.


Pour le compositeur d’idées, nous utilisons le TextField composable pour prendre en charge la saisie de texte. Ce composable contient un argument, visualTransformationqui est utilisé pour appliquer des modifications visuelles au texte saisi.

TextField(
    ...
    visualTransformation = ...
)

Cet argument nécessite une VisualTransformation implémentation utilisée pour appliquer la transformation visuelle au texte saisi. Si nous regardons le code source de cette interface, nous remarquerons une fonction de filtre qui prend le contenu du TextField et renvoie un TransformedText référence qui contient le texte modifié.

@Immutable
fun interface VisualTransformation {
    fun filter(text: AnnotatedString): TransformedText
}

En ce qui concerne ce texte modifié, nous sommes tenus de fournir l’implémentation qui crée un nouveau AnnotatedString référence avec nos modifications appliquées. Ce contenu modifié est ensuite regroupé dans le TransformedText taper et revenir à la TextField pour la composition.

Afin que nous puissions définir et appliquer des transformations au contenu de notre TextFieldnous devons commencer par créer une nouvelle implémentation de VisualTransformation interface pour laquelle nous allons créer une nouvelle classe, UrlTransformation. Cette classe implémentera VisualTransformation argument, ainsi que de prendre un seul argument sous la forme d’un Color. Nous définissons cet argument afin que nous puissions transmettre une référence de couleur de thème à appliquer dans notre logique, car nous allons être en dehors de la portée composable et n’aurons pas accès à notre thème composable.

class UrlTransformation(
    val color: Color
) : VisualTransformation {

}

Avec cette classe définie, nous devons maintenant implémenter la fonction de filtre à partir du VisualTransformation interface. Dans cette fonction, nous allons renvoyer une instance de TransformedText class – nous pouvons sauter dans le code source de cette classe et voir qu’il y a deux propriétés requises lors de l’instanciation de cette classe.

/**
 * The transformed text with offset offset mapping
 */
class TransformedText(
    /**
     * The transformed text
     */
    val text: AnnotatedString,

    /**
     * The map used for bidirectional offset mapping from original to transformed text.
     */
    val offsetMapping: OffsetMapping
)

Ces deux arguments sont requis, nous allons donc devoir fournir une valeur pour chacun lors de l’instanciation du TransformedText classer.

  • texte – ce sera la version modifiée du texte qui est fourni à la fonction de filtrage
  • offsetMapping – selon la documentation, il s’agit de la carte utilisée pour le mappage de décalage bidirectionnel du texte original au texte transformé
class UrlTransformation(
    val color: Color
) : VisualTransformation {
    override fun filter(text: AnnotatedString): TransformedText {
        return TransformedText(
            ...,
            OffsetMapping.Identity
        )
    }
}

Pour le offsetMapping argument, nous passons simplement le OffsetMapping.Identity valeur – il s’agit de la valeur par défaut prédéfinie utilisée pour OffsetMapping interface, utilisée lorsqu’elle peut être utilisée pour la transformation de texte qui ne modifie pas le nombre de caractères. En ce qui concerne l’argument de texte, nous devrons écrire une logique qui prendra le contenu actuel, appliquera la surbrillance et le renverra comme un nouveau AnnotatedString référence à transmettre à notre TransformedText référence. Pour cette logique, nous allons créer une nouvelle fonction, buildAnnotatedStringWithUrlHighlighting. Cela va prendre deux arguments – le texte qui doit être mis en surbrillance, ainsi que la couleur à utiliser pour la mise en surbrillance.

fun buildAnnotatedStringWithUrlHighlighting(
    text: String, 
    color: Color
): AnnotatedString {
    
}

A partir de cette fonction, nous devons retourner un AnnotatedString référence, que nous allons créer en utilisant buildAnnotatedString. Dans cette fonction, nous commencerons par utiliser l’opération d’ajout pour définir le contenu textuel du AnnotatedString.

fun buildAnnotatedStringWithUrlHighlighting(
    text: String, 
    color: Color
): AnnotatedString {
    return buildAnnotatedString {
        append(text)
    }
}

Ensuite, nous devrons prendre le contenu de notre chaîne et appliquer la surbrillance à toutes les URL présentes. Avant de pouvoir le faire, nous devons identifier les URL dans la chaîne. La détection d’URL peut varier en fonction du cas d’utilisation, donc pour garder les choses simples, écrivons un exemple de code qui trouvera les URL dans un morceau de texte donné. Ce code prendra la chaîne donnée et filtrera les URL, fournissant une liste de chaînes d’URL comme résultat.

text?.split("\\s+".toRegex())?.filter { word ->
    Patterns.WEB_URL.matcher(word).matches()
}

Maintenant que nous savons quelles URL se trouvent dans la chaîne, nous allons devoir leur appliquer une surbrillance. Cela va être sous la forme d’un style de chaîne annoté, qui est appliqué à l’aide de l’opération addStyle.

fun addStyle(style: SpanStyle, start: Int, end: Int)

Lors de l’appel de cette fonction, nous devons passer le SpanStyle que nous souhaitons appliquer, ainsi que les index de début et de fin auxquels ce style doit être appliqué. Nous allons commencer par calculer cet index de début et de fin – pour simplifier les choses, nous allons supposer qu’il n’y a que des URL uniques dans notre chaîne.

text?.split("\\s+".toRegex())?.filter { word ->
    Patterns.WEB_URL.matcher(word).matches()
}.forEach {
    val startIndex = text.indexOf(it)
    val endIndex = startIndex + it.length
}

Ici, nous localisons l’index de départ en utilisant le indexOf fonction, qui nous donnera l’index de départ de l’URL donnée. Nous utiliserons ensuite cet index de début et la longueur de l’URL pour calculer l’index de fin. Nous pouvons ensuite passer ces valeurs aux arguments correspondants pour le addStyle fonction.

text?.split("\\s+".toRegex())?.filter { word ->
    Patterns.WEB_URL.matcher(word).matches()
}.forEach {
    val startIndex = text.indexOf(it)
    val endIndex = startIndex + it.length
    addStyle(
        start = startIndex, 
        end = endIndex
    )
}

Ensuite, nous devons fournir le SpanStyle que nous voulons appliquer à la plage d’indices donnée. Ici, nous voulons simplement mettre en surbrillance le texte en utilisant la couleur fournie, nous allons donc passer la valeur de couleur de nos arguments de fonction comme argument de couleur pour le SpanStyle fonction.

text?.split("\\s+".toRegex())?.filter { word ->
    Patterns.WEB_URL.matcher(word).matches()
}.forEach {
    val startIndex = text.indexOf(it)
    val endIndex = startIndex + it.length
    addStyle(
        style = SpanStyle(
            color = color
        ),
        start = startIndex, 
        end = endIndex
    )
}

Avec cela en place, nous avons maintenant une fonction complète qui prendra le texte fourni et mettra en évidence toutes les URL à l’aide du Color référence.

fun buildAnnotatedStringWithUrlHighlighting(
    text: String, 
    color: Color
): AnnotatedString {
    return buildAnnotatedString {
        append(text)
        text?.split("\\s+".toRegex())?.filter { word ->
            Patterns.WEB_URL.matcher(word).matches()
        }.forEach {
            val startIndex = text.indexOf(it)
            val endIndex = startIndex + it.length
            addStyle(
                style = SpanStyle(
                    color = color,
                    textDecoration = TextDecoration.None
                ),
                start = startIndex, end = endIndex
            )
        }
    }
}

Nous devrons alors remonter dans notre UrlTransformation classe et transmettre le résultat de la buildAnnotatedStringWithUrlHighlighting appel de fonction pour le TransformedText dispute.

class UrlTransformation(
    val color: Color
) : VisualTransformation {
    override fun filter(text: AnnotatedString): TransformedText {
        return TransformedText(
            buildAnnotatedStringWithUrlHighlighting(text, color),
            OffsetMapping.Identity
        )
    }
}

Maintenant que notre UrlTransformation l’implémentation est terminée, nous pouvons l’instancier et passer la référence pour le visualTransformation argumentation de la TextField composable. Ici, nous utilisons la couleur désirée de notre MaterialTheme référence, qui sera utilisée lors de la mise en évidence des URL dans notre TextField contenu.

TextField(
    ...
    visualTransformation = UrlTransformation(
        MaterialTheme.colors.secondary)
)

Avec ce qui précède en place, nous avons maintenant la prise en charge de la mise en évidence d’URL dynamique dans notre TextField composable. Cela signifie que désormais, chaque fois que l’utilisateur insère une URL dans le composeur pour une idée, nous l’identifions comme une URL en la mettant en surbrillance à l’aide de la couleur secondaire de notre thème.

Dans cet article, nous avons appris comment appliquer la mise en évidence d’URL dynamique au contenu d’un TextField composable. Dans le prochain article, nous verrons comment nous avons ajouté la fenêtre contextuelle « Ouvrir le lien » lorsqu’une URL est tapée dans la zone de saisie du compositeur.






Source link

décembre 13, 2022