Affichage de texte Android


L'affichage d'informations textuelles est probablement la partie la plus fondamentale et la plus importante de nombreuses applications Android. Cet article parlera de TextView. Chaque développeur, à commencer par «Hello World», est constamment confronté à cet élément d'interface utilisateur. De temps en temps, lorsque vous travaillez avec du texte, vous devez penser à implémenter diverses solutions de conception ou à améliorer les performances lors du rendu de l'écran.


Je vais parler du périphérique TextView et de certaines subtilités de son utilisation. Des conseils clés ont été tirés de rapports sur les anciennes E / S Google


TextView sous le capot


Pour le rendu de texte dans Android, une pile entière de bibliothèques différentes est utilisée sous le capot. Ils peuvent être divisés en deux parties principales - le code java et le code natif:



Le code Java fait essentiellement partie du SDK Android disponible pour les développeurs d'applications et ses nouvelles fonctionnalités peuvent être portées vers la bibliothèque de support.


Le noyau TextView lui-même est écrit en C ++, ce qui limite le portage à la bibliothèque de support des nouvelles fonctionnalités implémentées à partir de nouvelles versions du système d'exploitation. Le noyau est constitué des bibliothèques suivantes:


  • Minikin est utilisĂ© pour mesurer la longueur du texte, des sauts de ligne et des mots par syllabes.
  • ICU fournit un support Unicode.
  • HarfBuzz trouve pour les caractères Unicode les Ă©lĂ©ments graphiques (glyphes) correspondants dans les polices.
  • FreeType crĂ©e des bitmaps de glyphes.
  • Skia est un moteur pour dessiner des graphiques 2D.

Mesurer la longueur du texte et les sauts de ligne


Si vous passez la ligne à la bibliothèque Minikin, qui est utilisée à l'intérieur de TextView, la première chose qu'elle détermine est de savoir quels glyphes la ligne se compose:



Comme vous pouvez le voir dans cet exemple, faire correspondre les caractères Unicode avec les glyphes ne sera pas toujours un à un: ici, 3 caractères à la fois correspondront à un glyphe ffi. De plus, il convient de noter que les glyphes nécessaires peuvent être trouvés dans différentes polices système.


Trouver des glyphes uniquement dans les polices système peut entraîner des difficultés, en particulier si les icônes ou les emojis sont affichés à travers les caractères, et il est censé combiner les caractères de différentes polices sur une seule ligne. Par conséquent, à partir d' Android Q (29) , il est devenu possible de créer votre propre liste de polices fournies avec l'application. Cette liste sera utilisée pour rechercher des glyphes:


 textView.typeface = TypeFace.CustomFallbackBuilder( FontFamily.Builder( Font.Builder(assets, “lato.ttf”).build() ).build() ).addCustomFallback( FontFamily.Builder( Font.Builder(assets, “kosugi.ttf”).build() ).build() ).build() 

Désormais, à l'aide de CustomFallbackBuilder lors de la mise en correspondance de caractères avec des glyphes, le SDK CustomFallbackBuilder la famille de polices spécifiée dans l'ordre, et s'il est introuvable, la recherche se poursuit dans les polices système (et grâce à la méthode setSystemFallback() , vous pouvez spécifier la famille de polices système préférée). CustomFallbackBuilder a une limite sur le nombre de familles de polices - vous ne pouvez pas ajouter plus de 64 polices.


La bibliothèque Minikin divise les chaînes en mots et mesure les mots individuels. Pour accélérer le travail, en commençant par Lollipop (21) , un cache système LRU de mots est utilisé. Un tel cache donne un énorme gain de performances: un appel à Paint.measureText() pour un mot mis en cache prendra en moyenne 3% du temps lors du premier calcul de sa taille.



Si le texte ne correspond pas à la largeur spécifiée, Minikin organise les sauts de ligne et les mots dans le texte. À partir de Marshmallow (23), vous pouvez contrôler son comportement en spécifiant les attributs spéciaux breakStrategy et hyphenationFrequency pour TextView.


Avec la valeur breakStrategy=simple bibliothèque arrangera simplement les tirets séquentiellement, en passant par le texte: dès que la ligne cesse de tenir, la césure est placée avant le dernier mot.



Dans la valeur balanced bibliothèque essaiera de faire des sauts de ligne afin que les lignes soient alignées en largeur.



high_quality a presque le même comportement que balanced , à l'exception de quelques différences (l'une d'entre elles: sur l'avant-dernière ligne, la césure peut être non seulement des mots séparés, mais aussi des mots par syllabes).


L'attribut hyphenationFrequency permet de contrôler la stratégie d'habillage des mots par syllabes. Une valeur none ne fera pas de césure automatique, normal fera une petite fréquence de césure et full , en conséquence, utilisera le nombre maximum de mots.



Performances de rendu du texte en fonction des indicateurs sélectionnés (mesurées sur Android P (28) ):



Compte tenu d'une performance assez forte, les développeurs de Google, à partir de la version Q (29) et d' AppCompat 1.1.0 , ont décidé de désactiver la césure par défaut. Si l'habillage de mots est important dans l'application, vous devez maintenant l'activer explicitement.


Lors de l'utilisation de l'habillage de mots, il faut tenir compte du fait que la langue actuellement sélectionnée dans le système d'exploitation affectera le fonctionnement de la bibliothèque. Selon la langue, le système sélectionnera des dictionnaires spéciaux avec des règles de transfert.


Styles de texte


Il existe plusieurs façons de styliser du texte dans Android:


  • Un style unique qui s'applique Ă  l'ensemble de l'Ă©lĂ©ment TextView.
  • Multi-style (multi style) - plusieurs styles Ă  la fois, qui peuvent ĂŞtre appliquĂ©s au texte, au niveau du paragraphe ou des caractères individuels. Il existe plusieurs façons de procĂ©der:
    • dessin de texte sur toile
    • balises html
    • Ă©lĂ©ments de balisage spĂ©ciaux - Ă©tendues

Un style unique implique l'utilisation de styles XML ou d'attributs XML dans le balisage TextView. Dans ce cas, le système appliquera les valeurs des ressources dans l'ordre suivant: Apparence du texte, thème (Thème), style par défaut (Style par défaut), style de l'application, et la priorité la plus élevée est les valeurs des attributs View.


L'utilisation des ressources est une solution assez simple, mais, malheureusement, elle ne vous permet pas d'appliquer du style Ă  certaines parties du texte.


Les balises HTML sont une autre solution simple qui fournit des fonctionnalités telles que la mise en gras de mots individuels, l'italique ou même la mise en évidence de listes avec des points dans le texte. Tout ce dont le développeur a besoin est d'appeler la méthode Html.fromHtml() , qui transformera le texte balisé en texte balisé par des étendues. Mais cette solution a des capacités limitées, car elle ne reconnaît qu'une partie des balises html et ne prend pas en charge les styles CSS.


 val text = "My text <ul><li>bullet one</li><li>bullet two</li></ul>" myTextView.text = Html.fromHtml(text) 

Différentes méthodes de style TextView peuvent être combinées, mais il convient de se rappeler la priorité d'une méthode particulière, qui affectera le résultat final:



Une autre façon - dessiner du texte sur la toile - donne au développeur un contrôle total sur la sortie de texte: par exemple, vous pouvez dessiner du texte le long d'une ligne courbe. Mais une telle solution, selon les exigences, peut être assez difficile à mettre en œuvre et dépasse le cadre de cet article.


Portées


TextView utilise des étendues pour affiner les styles. En utilisant des étendues, vous pouvez changer la couleur d'une plage de caractères, faire une partie du texte sous forme de liens, changer la taille du texte, dessiner un point devant un paragraphe, etc.


On peut distinguer les catégories de portées suivantes:


  • PortĂ©es de caractères - appliquĂ©es au niveau des caractères d'une chaĂ®ne.
    • Apparence affectant - ne modifiez pas la taille du texte.
    • Affectation mĂ©trique - redimensionner le texte.
  • PortĂ©es de paragraphe - appliquĂ©es au niveau du paragraphe.


Le framework Android a des interfaces et des classes abstraites avec des méthodes qui sont appelées pendant onMeasure() et le rendu de TextView, ces méthodes permettent aux travées d'accéder à des objets de niveau inférieur comme TextPaint et Canvas . En utilisant l'étendue, le framework Android vérifie quelles interfaces cet objet implémente pour appeler les méthodes nécessaires.



Le cadre Android définit environ 20+ portées, donc avant de créer le vôtre, il est préférable de vérifier si le SDK convient.


Apparence vs métrique affectant les portées


La première catégorie de plages affecte l'apparence des caractères de la chaîne: couleur des caractères, couleur d'arrière-plan, caractères soulignés ou barrés, etc. Ces UpdateAppearance implémentent l'interface UpdateAppearance et héritent de la classe CharacterStyle , qui permet d'accéder à l'objet TextPaint .



Les mesures affectant la durée affectent la taille du texte et de la mise en page.Par conséquent, l'utilisation d'une telle durée nécessite non seulement de redessiner le TextView, mais également l'appel de onMeasure() / onLayout() . Ces plages sont généralement héritées de la classe MetricAffectingSpan , qui hérite du CharacterStyle mentionné ci-dessus.



Caractère vs paragraphe affectant les portées


La portée du paragraphe affecte un bloc de texte entier: il peut modifier l'alignement, le retrait ou même insérer un point au début d'un paragraphe. Ces plages doivent être héritées de la classe ParagraphStyle et insérées dans le texte exactement du début du paragraphe jusqu'à sa fin. Si la plage est incorrecte, la plage ne fonctionnera pas.



Sur Android, les paragraphes sont considérés comme la partie du texte séparée par des retours à la ligne ( \n ).



Écrire vos échelles


Lors de l'écriture de vos propres plages, vous devez décider de ce que la plage affectera afin de choisir de quelle classe hériter:


  • Affecte le texte au niveau du caractère -> CharacterStyle
  • Affecte le texte au niveau du paragraphe -> ParagraphStyle
  • Affecte l'affichage du texte → UpdateAppearance
  • Affecte la taille du texte - UpdateLayout

Voici un exemple de durée pour changer la police:


 class CustomTypefaceSpan(private val font: Typeface?) : MetricAffectingSpan() { override fun updateMeasureState(textPaint: TextPaint) = update(textPaint) override fun updateDrawState(textPaint: TextPaint) = update(textPaint) fun update(textPaint: TextPaint) { textPaint.apply { val old = typeface val oldStyle = old?.style ?: 0 val font = Typeface.create(font, oldStyle) typeface = font //    } } } 

Imaginez que nous voulons créer notre propre étendue pour mettre en évidence des blocs de code, pour cela, nous modifierons notre étendue précédente - après avoir ajouté la police, nous ajouterons également un changement dans la couleur d'arrière-plan du texte:


 class CodeBlockSpan(private val font: Typeface?) : MetricAffectingSpan() { … fun update(textPaint: TextPaint) { textPaint.apply { //    … bgColor = lightGray //    } } } 

Appliquez span au texte:


 //    span spannable.setSpan(CodeBlockSpan(typeface), ...) 

Mais vous pouvez obtenir exactement le même résultat en combinant deux CustomTypefaceSpan : prenez nos précédents CustomTypefaceSpan et BackgroundColorSpan du framework Android:


 //    spannable.setSpan(BackgroundColorSpan(lightGray), ...) //   spannable.setSpan(CustomTypefaceSpan(typeface), ...) 

Ces deux solutions auront une différence. Le fait est que les plages auto-écrites ne peuvent pas implémenter l'interface Parcelable , contrairement à celles du système.


Lors de la transmission d'une ligne stylisée via Intent ou le presse-papiers en cas de plage de balisage auto-écrite ne sera pas enregistrée. Lorsque vous utilisez des étendues du framework, le balisage reste.



Utilisation de portées dans le texte


Il existe deux interfaces pour le texte stylisé dans le cadre: Spanned et Spannable (avec un balisage inchangé et mutable, respectivement) et trois implémentations: SpannedString (texte inchangé), SpannableString (texte inchangé) et SpannableStringBuilder (texte mutable).


Texte mutableBalisage variable
Chaîne étenduenonnon
Chaîne extensiblenonoui
Constructeur Spannablestringouioui

SpannableStringBuilder , par exemple, est utilisé dans un EditText qui doit changer de texte.


Vous pouvez ajouter une nouvelle étendue à une ligne en utilisant la méthode:


setSpan(Object what, int start, int end, int flags)


La plage est passée par le premier paramètre, puis la plage d'indices dans le texte est indiquée. Et le dernier paramètre peut être contrôlé, quel sera le comportement de la plage lors de l'insertion de nouveau texte: si la plage s'étendra au texte inséré au début ou à la fin (si vous insérez un nouveau texte au milieu, la plage s'y appliquera automatiquement, quelles que soient les valeurs d'indicateur) .


Les classes répertoriées ci-dessus diffèrent non seulement sur le plan sémantique, mais également sur la façon dont elles sont organisées en interne: SpannedString et SpannableString utilisent des tableaux pour stocker des plages, et SpannableStringBuilder utilise une arborescence d'intervalles .


Si vous effectuez des tests pour la vitesse de rendu du texte en fonction du nombre de plages, vous obtiendrez les résultats suivants: lorsque vous utilisez jusqu'à ~ 250 plages consécutives, SpannableString et SpannableStringBuilder fonctionnent approximativement à la même vitesse, mais si les éléments de balisage dépassent 250, SpannableString démarre à perdre. Ainsi, si la tâche consiste à appliquer un style à du texte, alors lors du choix d'une classe, il faut être guidé par des exigences sémantiques: si la ligne et les styles seront modifiables. Mais si le balisage nécessite plus de 250 étendues, vous devez toujours donner la SpannableStringBuilder à SpannableStringBuilder .


Vérifier la durée dans le texte


La tâche se produit périodiquement pour vérifier si une ligne étendue a une étendue spécifique. Et sur Stackoverflow, vous pouvez trouver ce code:


 fun <T> hasSpan(spanned: Spanned, clazz: Class<T>): Boolean { val spans: Array<out T> = spanned.getSpans(0, spanned.length, clazz) return spans.isNotEmpty() } 

Une telle solution fonctionnera, mais elle est inefficace: vous devez parcourir toutes les étendues, vérifier si chacune d'entre elles appartient au type passé, collecter le résultat dans un tableau et, à la fin, vérifier simplement que le tableau n'est pas vide.


Une solution plus efficace serait d'utiliser la méthode nextSpanTransition() :


 fun <T> hasSpan(spanned: Spanned, clazz: Class<T>): Boolean { val limit = spanned.length return spanned.nextSpanTransition(0, limit, clazz) < limit } 

Balisage de texte dans diverses ressources linguistiques


Une telle tâche peut se produire lorsque vous souhaitez mettre en évidence un mot spécifique à l'aide du balisage dans diverses ressources de chaîne. Par exemple, nous devons mettre en évidence le mot «text» dans la version anglaise et «texto» dans l'espagnol:


 <!-- values-en/strings.xml --> <string name="title">Best practices for text in Android</string> <!-- values-es/strings.xml --> <string name=”title”>Texto en Android: mejores prácticas</string> 

Si vous avez besoin de quelque chose de simple, par exemple, pour surligner le mot en gras, vous pouvez utiliser les balises html habituelles ( <b> ). Dans l'interface utilisateur, il vous suffit de définir la ressource de chaîne dans TextView:


 textView.setText(R.string.title) 

Mais si vous avez besoin de quelque chose de plus complexe, par exemple, changer la police, alors le HTML ne peut plus être utilisé. La solution consiste à utiliser la <annotation> spéciale. Cette balise vous permet de définir n'importe quelle paire clé-valeur dans un fichier xml. Lorsque nous tirons une chaîne des ressources, ces balises sont automatiquement converties en plages d' Annotation , organisées dans le texte avec les clés et les valeurs correspondantes. Après cela, vous pouvez analyser la liste des annotations dans le texte et appliquer les étendues nécessaires.


Supposons que nous devons changer la police Ă  l'aide de CustomTypefaceSpan .


Ajoutez une balise et définissez une clé de «police» et une valeur - le type de police que nous voulons utiliser est «title_emphasis» :

 <!-- values-en/strings.xml --> <string name="title">Best practices for <annotation font=”title_emphasis”>text</annotation> in Android</string> <!-- values-es/strings.xml --> <string name=”title”><annotation font=”title_emphasis”>Texto</annotation> en Android: mejores prácticas</string> 

Tirez la chaîne des ressources, trouvez les annotations avec la touche «police» et organisez les portées:


 //      SpannedString,     span' val titleText = getText(R.string.title) as SpannedString //    val annotations = titleText.getSpans(0, titleText.length, Annotation::class.java) //     SpannableString //      val spannableString = SpannableString(titleText) //     for (annotation in annotations) { //     "font" if (annotation.key == "font") { val fontName = annotation.value //   ,     if (fontName == "title_emphasis") { val typeface = getFontCompat(R.font.permanent_marker) //  span    ,    spannableString.setSpan( CustomTypefaceSpan(typeface), titleText.getSpanStart(annotation), titleText.getSpanEnd(annotation), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE ) } } } styledText.text = spannableString 


Il a été mentionné ci-dessus que les Parcelable extérieures au cadre Android ne peuvent pas implémenter Parcelable et sont transmises via Intent. Mais cela ne s'applique pas aux annotations qui implémentent Parcelable . Vous pouvez donc passer la chaîne annotée via Intent et analyser exactement de la même manière en organisant vos étendues.


Comment le texte est placé dans un TextView


TextView peut afficher non seulement du texte, mais aussi des images. Vous pouvez également définir différents retraits devant le texte. Sous le capot, cela fonctionne pour que TextView crée une classe enfant, Layout, qui est directement responsable de l'affichage du texte. Il s'agit d'une classe abstraite qui comporte trois implémentations; généralement, vous n'avez pas à travailler directement avec elles, sauf si vous écrivez votre propre contrôle:


  • BoringLayout est utilisĂ© pour les textes simples, ne prend pas en charge les sauts de ligne, RTL et autres, mais il est le plus lĂ©ger. TextView l'utilise si le texte respecte toutes les restrictions.
  • StaticLayout est utilisĂ© dans TextView pour d'autres cas.
  • DynamicLayout est utilisĂ© pour le texte modifiable dans un EditText.

La mise en page a de nombreuses méthodes qui vous permettent de connaître les différents paramètres du texte affiché: les coordonnées des lignes, la ligne de base, les coordonnées du début et de la fin du texte dans la ligne, etc. (plus de détails peuvent être trouvés dans la documentation )


Ces méthodes peuvent être très utiles. Par exemple, certains développeurs sont confrontés à la tâche d'extraire une partie du texte en rectangles arrondis et d'essayer de trouver sa solution à travers des étendues qui ne sont pas applicables pour résoudre ce problème.



Mais les méthodes de la classe Layout peuvent venir à la rescousse. Voici un exemple de solution:


À l'aide d'annotations, nous sélectionnons les mots qui doivent être encerclés dans des rectangles.


Créez ensuite 4 ressources dessinables pour tous les cas d'habillage de texte, qui doivent être entourées de rectangles:



Ensuite, nous trouvons les annotations dont nous avons besoin dans le texte, comme décrit ci-dessus. Nous avons maintenant les index du début et de la fin d'une telle annotation. Grâce aux méthodes de mise en page, vous pouvez connaître le numéro de la ligne sur laquelle le texte annoté commence et se termine:


 val startLine = layout.getLineForOffset(spanStartIndex) val endLine = layout.getLineForOffset(spanEndIndex) 

Ensuite, vous devez dessiner un ou plusieurs rectangles. Prenons le cas simple où la partie annotée du texte est apparue sur une seule ligne, alors nous n'avons besoin que d'un rectangle avec quatre coins arrondis. Définissez ses coordonnées et dessinez:


 ... if (startLine == endLine) { val lineTop = layout.getLineTop(startLine) //    val lineBottom = layout.getLineBottom(startLine) //    val startCoor = layout.getPrimaryHorizontal(spanStartIndex).toInt() //    val endCoor = layout.getPrimaryHorizontal(spanEndIndex).toInt() //    //   drawable.setBounds(startCoor, lineTop, endCoor, lineBottom) drawable.draw(canvas) ... 

Comme vous pouvez le voir dans cet exemple, Layout stocke de nombreuses informations utiles sur le texte affiché, ce qui peut aider à la mise en œuvre de diverses tâches non standard.


Performances de TextView


TextView, comme toute vue, passe par trois phases lorsqu'il est affiché: onMeasure() , onLayout() et onDraw() . En même temps, onMeasure() prend le plus de temps, contrairement aux deux autres méthodes: à ce moment, la classe Layout est recréée et la taille du texte est calculée. Changer la taille du texte (par exemple, changer la police) demande donc beaucoup de travail. Changer la couleur du texte sera plus léger car il ne nécessite que d'appeler onDraw() . Comme mentionné ci-dessus, le système dispose d'un cache de mots global avec des tailles calculées. Si le mot est déjà dans le cache, appeler à nouveau onMeasure() pour cela prendra 11-16% du temps qui aurait été nécessaire pour un calcul complet.


Accélération du texte


En 2015, les développeurs Instagram ont accéléré l'affichage des commentaires sur les photos en utilisant le cache global. L'idée était de dessiner virtuellement le texte avant de l'afficher à l'écran, «réchauffant» ainsi le cache du système. Lorsqu'il était temps d'afficher le texte, l'utilisateur le voyait beaucoup plus rapidement, car le texte était déjà mesuré et était dans le cache.


À partir d' Android P (28) , les développeurs de Google ont ajouté à l'API la possibilité d'effectuer la phase de mesure de la taille du texte à l'avance dans le thread d'arrière-plan - PrecomputedText (et le backport pour l'API commençant par Android I (14) - PrecomputedTextCompat ). En utilisant la nouvelle API, 90% du travail sera effectué dans le thread d'arrière-plan.


Un exemple:


 // UI thread val params: PrecomputedText.Params = textView.getTextMetricsParams() val ref = WeakReference(textView) executor.execute { // background thread val text = PrecomputedText.create("Hello", params) val textView = ref.get() textView?.post { // UI thread val textView = ref.get() textView?.text = text } } 

Afficher le texte en grand


Si vous devez afficher un texte volumineux, ne le transférez pas immédiatement vers un TextView. Sinon, l'application peut cesser de fonctionner en douceur ou se figer complètement, car elle fera beaucoup de travail sur le thread principal pour afficher un texte énorme que l'utilisateur peut même ne pas faire défiler jusqu'à la fin. La solution consiste à diviser le texte en parties (par exemple, des paragraphes) et à afficher les parties individuelles dans RecyclerView. Pour une accélération encore plus grande, vous pouvez pré-calculer la taille des blocs de texte à l'aide de PrecomputedText.


Pour faciliter l'incorporation de PrecomputedText dans RecyclerView, les développeurs Google ont créé des méthodes spéciales PrecomputedTextCompat.getTextFuture() et AppCompatTextView.setTextFuture() :


 fun onBindViewHolder(vh: ViewHolder, position: Int) { val data = getData(position) vh.textView.setTextSize(...) vh.textView.setFontVariationSettings(...) //    val future = PrecomputedTextCompat.getTextFuture( data.text, vh.textView.getTextMetricsParamsCompat(), myExecutor ) //  future  TextView,      onMeasure() vh.textView.setTextFuture(future) } 

RecyclerView , , , .


, getTextFuture() (, ), , , getTextFuture() , , TextView.


, TextView


TextView.setText() :


 if (type == SPANNABLE || movementMethod != null) { text = spannableFactory.newSpannable(spannable) //  } else { text = new SpannedString(spannable) //  } 

span' TextView, setText() , .


, . TextView , -, . , . , , TextView spannableFactory :


 class MySpannableFactory : Spannable.Factory() { override fun newSpannable(source: CharSequence): Spannable { return source as? Spannable ?: super.newSpannable(source) } } textView.spannableFactory = MySpannableFactory() 

textView.setText(spannable, BufferType.SPANNABLE) , .


Google span' RecyclerView, .


TextView, span, setText() . TextView span. TextView spannable- span', :


 val spannable = textView.getText() as Spannable val span = CustomTypefaceSpan(span) spannable.setSpan(span, ...) 

span, TextView, TextView . , invalidate() , – requestLayout() :


 val spannable = textView.getText() as Spannable val span = CustomTypefaceSpan(span) spannable.setSpan(span, ...) span.setTypeface(anotherTypeface) textView.requestLayout() // re-measure and re-draw // or textView.invalidate() // re-draw 


TextView . autoLink . autoLink=”web” TextView URL URLSpan . , SDK setText() :


 spannable = new SpannableString(string); Matcher m = pattern.matcher(text); while (...) { //      String utl = … URLSpan span = new URLSpan(url); spannable.setSpan(span, ...); } 

UI , autoLink=”web” RecyclerView. . LinkifyCompat :


 //  ,       background thread val spannable = SpannableString(string) LinkifyCompat.addLinks(spannable, Linkify.WEB_URLS) //   RecyclerView override fun onBindViewHolder(holder: ViewHolder, position: Int) { holder.textView.setText(spannable, BufferType.SPANNABLE) // ... } 

autoLink map – ( all ). . , WebView, ! SDK Linkify.gatherMapLinks() , :


 while ((address = WebView.findAddress(string)) != null) { ... } 

WebView TODO SDK:


 public static String findAddress(String addr) { // TODO: Rewrite this in Java so it is not needed to start up chromium // Could also be deprecated return getFactory().getStatics().findAddress(addr); } 

? Smart Linkify, Android P (28) , , . :


 // UI thread val text: Spannable = … val request = TextLinks.Request.Builder(text) val ref = WeakReference(textView) executor.execute { // background thread TextClassifier.generateLinks(request).apply(text) val textView = ref.get() textView?.post { // UI thread val textView = ref.get() textView?.text = text } } 

Linkify, . toolbar , Google .


Smart Linkify : , .



Magnifier


Android P (28) , – Magnifier, . .



TextView, EditText WebView, : API .


Conclusion


Android , , :


  • , TextView (, EditText)

- , Google I/O'19 “Best Practices for Using Text in Android” .



Liens utiles


Les articles



Rapports


Source: https://habr.com/ru/post/fr461787/


All Articles