Spans sind leistungsstarke Markup-Objekte, mit denen Sie Text an der
Zeichen- oder Absatzebene enthalten. Durch das Anhängen von Spans an Textobjekte können Sie
Text hinzufügen, z. B. durch Hinzufügen von Farbe,
Skalieren der Textgröße und benutzerdefiniertes Zeichnen von Text. Spans können auch
TextPaint
-Eigenschaften ändern, auf einem
Canvas
und ändern Sie das Textlayout.
Android bietet mehrere Arten von Spans, die eine Vielzahl von gängigen Texten abdecken. und Stilmustern. Sie können auch eigene Spans erstellen, um benutzerdefinierte Stile anzuwenden.
.Span erstellen und anwenden
Zum Erstellen von Spans können Sie eine der in der folgenden Tabelle aufgeführten Klassen verwenden. Die Klassen unterscheiden sich je nachdem, ob der Text selbst änderbar ist, das Markup änderbar ist und welche zugrunde liegende Datenstruktur die Span-Daten enthält.
Klasse | Veränderlicher Text | Veränderliches Markup | Datenstruktur |
---|---|---|---|
SpannedString |
Nein | Nein | Lineares Array |
SpannableString |
Nein | Ja | Lineares Array |
SpannableStringBuilder |
Ja | Ja | Intervallstruktur |
Alle drei Klassen erweitern die Spanned
. SpannableString
und SpannableStringBuilder
erweitern außerdem die
Spannable
-Oberfläche.
So entscheiden Sie, welche Version Sie verwenden möchten:
- Falls Sie den Text oder das Markup nach der Erstellung nicht ändern, verwenden Sie
SpannedString
- Wenn Sie eine kleine Anzahl von Spans an ein einzelnes Textobjekt
der Text selbst schreibgeschützt ist, verwenden Sie
SpannableString
. - Wenn Sie Text nach der Erstellung ändern und Spans an
den Text, verwenden Sie
SpannableStringBuilder
. - Wenn Sie eine große Anzahl von Spans an ein Textobjekt anhängen müssen,
ob der Text schreibgeschützt ist, verwende
SpannableStringBuilder
.
Rufen Sie setSpan(Object _what_, int _start_, int _end_, int
_flags_)
auf, um einen Span anzuwenden.
für ein Spannable
-Objekt. Der Parameter what bezieht sich auf die entsprechende Spanne.
auf den Text angewendet und die Parameter start und end geben den Teil an,
des Textes, auf den Sie die Spanne anwenden.
Wenn Sie Text innerhalb der Grenzen eines Spans einfügen, wird dieser automatisch zu
den eingefügten Text enthält. Wenn Sie Text an der Spanne einfügen
Grenzen, d. h. am start- oder end-Index, den Flags
legt fest, ob die Spanne so erweitert wird, dass sie den eingefügten Text enthält. Verwenden Sie
die
Spannable.SPAN_EXCLUSIVE_INCLUSIVE
Flag, um eingefügten Text einzuschließen, und verwenden Sie
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
um den eingefügten Text auszuschließen.
Das folgende Beispiel zeigt, wie ein
ForegroundColorSpan
in ein
String:
Kotlin
val spannable = SpannableStringBuilder("Text is spantastic!") spannable.setSpan( ForegroundColorSpan(Color.RED), 8, // start 12, // end Spannable.SPAN_EXCLUSIVE_INCLUSIVE )
Java
SpannableStringBuilder spannable = new SpannableStringBuilder("Text is spantastic!"); spannable.setSpan( new ForegroundColorSpan(Color.RED), 8, // start 12, // end Spannable.SPAN_EXCLUSIVE_INCLUSIVE );
Da der Span mit Spannable.SPAN_EXCLUSIVE_INCLUSIVE
festgelegt wird, gilt der Span
wird erweitert, sodass an den Span-Grenzen eingefügter Text enthalten ist, wie in den
folgendes Beispiel:
Kotlin
val spannable = SpannableStringBuilder("Text is spantastic!") spannable.setSpan( ForegroundColorSpan(Color.RED), 8, // start 12, // end Spannable.SPAN_EXCLUSIVE_INCLUSIVE ) spannable.insert(12, "(& fon)")
Java
SpannableStringBuilder spannable = new SpannableStringBuilder("Text is spantastic!"); spannable.setSpan( new ForegroundColorSpan(Color.RED), 8, // start 12, // end Spannable.SPAN_EXCLUSIVE_INCLUSIVE ); spannable.insert(12, "(& fon)");
Sie können mehrere Spans an denselben Text anhängen. Das folgende Beispiel zeigt, um Text fett und rot zu erstellen:
Kotlin
val spannable = SpannableString("Text is spantastic!") spannable.setSpan(ForegroundColorSpan(Color.RED), 8, 12, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) spannable.setSpan( StyleSpan(Typeface.BOLD), 8, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE )
Java
SpannableString spannable = new SpannableString("Text is spantastic!"); spannable.setSpan( new ForegroundColorSpan(Color.RED), 8, 12, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE ); spannable.setSpan( new StyleSpan(Typeface.BOLD), 8, spannable.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE );
Android-Span-Typen
Android bietet mehr als 20 Span-Typen im android.text.style-Paket. Android kategorisiert Spans hauptsächlich auf zwei Arten:
- Auswirkung der Spanne auf den Text: Eine Spanne kann die Darstellung oder den Text von Text beeinflussen Messwerte.
- Span-Bereich: Einige Spans können auf einzelne Zeichen angewendet werden, während andere auf einen ganzen Absatz angewendet werden.
In den folgenden Abschnitten werden diese Kategorien ausführlicher beschrieben.
Spans, die sich auf die Textdarstellung auswirken
Einige Spans auf Zeichenebene wirken sich auf die Textdarstellung aus, z. B.
Text- oder Hintergrundfarbe ändern und Unter- und Unterstreichungen hinzufügen Diese
Spans erweitern den
Klasse CharacterStyle
.
Das folgende Codebeispiel zeigt, wie Sie mit UnderlineSpan
unterstreichen.
Text:
Kotlin
val string = SpannableString("Text with underline span") string.setSpan(UnderlineSpan(), 10, 19, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
Java
SpannableString string = new SpannableString("Text with underline span"); string.setSpan(new UnderlineSpan(), 10, 19, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
Spans, die nur die Textdarstellung betreffen, lösen eine Neuzeichnung des Textes ohne
das eine Neuberechnung des Layouts auslöst. Diese Spans implementieren
UpdateAppearance
und erweitern
CharacterStyle
Abgeleitete CharacterStyle
-Klassen definieren, wie Text gezeichnet wird, indem sie Zugriff auf
TextPaint
aktualisieren.
Spans, die sich auf Textmesswerte auswirken
Andere Spannen auf Zeichenebene wirken sich auf Textmesswerte aus, z. B.
und Textgröße. Diese Spannen erweitern die
MetricAffectingSpan
.
Mit dem folgenden Codebeispiel wird ein
RelativeSizeSpan
, die
erhöht die Textgröße um 50%:
Kotlin
val string = SpannableString("Text with relative size span") string.setSpan(RelativeSizeSpan(1.5f), 10, 24, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
Java
SpannableString string = new SpannableString("Text with relative size span"); string.setSpan(new RelativeSizeSpan(1.5f), 10, 24, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
Die Anwendung eines Spans, der sich auf Textmesswerte auswirkt, führt dazu, dass ein beobachtendes Objekt Messen Sie den Text erneut, um ein korrektes Layout und Rendering zu gewährleisten. Ändern Sie beispielsweise kann es vorkommen, dass Wörter in verschiedenen Zeilen stehen. Anwenden der vorstehenden span löst eine erneute Messung, Neuberechnung des Textlayouts und im Text.
Spans, die sich auf Textmesswerte auswirken, erweitern die MetricAffectingSpan
-Klasse, eine
Abstrakte Klasse, mit der abgeleitete Klassen definieren können, wie sich der Span auf die Textmessung auswirkt
durch Zugriff auf die TextPaint
. Da MetricAffectingSpan
CharacterSpan
beeinflussen die abgeleiteten Klassen die Darstellung des Textes am Zeichen.
Spans, die sich auf Absätze auswirken
Eine Spanne kann sich auch auf den Text auf Absatzebene auswirken, z. B. durch Ändern der
oder den Rand eines Textblocks. Spans, die ganze Absätze betreffen
ParagraphStyle
implementieren. Bis
diese Spans verwenden, werden sie an den gesamten Absatz angehängt, ohne das Ende
Zeilenumbruchzeichen. Wenn Sie versuchen, eine Absatzspanne auf etwas anderes als
einen ganzen Absatz beginnen, wendet Android diese Spanne überhaupt nicht an.
Abbildung 8 zeigt, wie Android Absätze im Text trennt.
<ph type="x-smartling-placeholder">Im folgenden Codebeispiel wird ein
QuoteSpan
zu einem Absatz hinzufügen. Beachten Sie, dass
wenn Sie die Span an einer anderen Position als am Anfang oder Ende eines
wird der Stil bei Android überhaupt nicht angewendet.
Kotlin
spannable.setSpan(QuoteSpan(color), 8, text.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
Java
spannable.setSpan(new QuoteSpan(color), 8, text.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
Benutzerdefinierte Spans erstellen
Wenn Sie mehr Funktionen benötigen, als in der bisherigen Android-Version können Sie benutzerdefinierte Spans implementieren. Entscheiden Sie beim Implementieren eines eigenen Spans ob sich die Spanne auf den Text auf Zeichen- oder Absatzebene auswirkt und und ob sie sich auf das Layout oder das Erscheinungsbild des Textes auswirken. Dies hilft Ihnen, Bestimmen, welche Basisklassen erweitert werden können und welche Schnittstellen Sie möglicherweise benötigen zu implementieren. Die folgende Tabelle dient als Referenz:
Szenario | Klasse oder Benutzeroberfläche |
---|---|
Die Spanne wirkt sich auf den Text auf Zeichenebene aus. | CharacterStyle |
Die Spanne beeinflusst die Textdarstellung. | UpdateAppearance |
Die Spanne wirkt sich auf die Textmesswerte aus. | UpdateLayout |
Die Spanne wirkt sich auf den Text auf Absatzebene aus. | ParagraphStyle |
Wenn Sie z. B. eine benutzerdefinierte Spanne implementieren müssen, die die Textgröße und
Farbe, RelativeSizeSpan
erweitern. Durch Übernahme RelativeSizeSpan
erweitert CharacterStyle
und implementiert die beiden Update
-Schnittstellen. Da diese
gibt bereits Callbacks für updateDrawState
und updateMeasureState
an.
können Sie diese Callbacks überschreiben, um Ihr benutzerdefiniertes Verhalten zu implementieren. Die
Mit dem folgenden Code wird ein benutzerdefinierter Span erstellt, der RelativeSizeSpan
und
überschreibt den updateDrawState
-Callback, um die Farbe von TextPaint
festzulegen:
Kotlin
class RelativeSizeColorSpan( size: Float, @ColorInt private val color: Int ) : RelativeSizeSpan(size) { override fun updateDrawState(textPaint: TextPaint) { super.updateDrawState(textPaint) textPaint.color = color } }
Java
public class RelativeSizeColorSpan extends RelativeSizeSpan { private int color; public RelativeSizeColorSpan(float spanSize, int spanColor) { super(spanSize); color = spanColor; } @Override public void updateDrawState(TextPaint textPaint) { super.updateDrawState(textPaint); textPaint.setColor(color); } }
In diesem Beispiel wird gezeigt, wie Sie einen benutzerdefinierten Span erstellen. Das erreichen Sie auch
indem Sie RelativeSizeSpan
und ForegroundColorSpan
auf den Text anwenden.
Span-Nutzung testen
Über die Spanned
-Schnittstelle können Sie sowohl Spans festlegen als auch Spans aus folgenden Quellen abrufen:
Text. Implementieren Sie beim Testen ein Android JUnit,
Test, um zu prüfen, ob die richtigen Spans hinzugefügt wurden
an den richtigen Stellen. Textstilbeispiel
App
enthält eine Span, die Markup auf Aufzählungspunkte anwendet, indem
BulletPointSpan
zum Text hinzufügen. Das folgende Codebeispiel zeigt, wie Sie
ob die Aufzählungspunkte wie erwartet angezeigt werden:
Kotlin
@Test fun textWithBulletPoints() { val result = builder.markdownToSpans("Points\n* one\n+ two") // Check whether the markup tags are removed. assertEquals("Points\none\ntwo", result.toString()) // Get all the spans attached to the SpannedString. val spans = result.getSpans<Any>(0, result.length, Any::class.java) // Check whether the correct number of spans are created. assertEquals(2, spans.size.toLong()) // Check whether the spans are instances of BulletPointSpan. val bulletSpan1 = spans[0] as BulletPointSpan val bulletSpan2 = spans[1] as BulletPointSpan // Check whether the start and end indices are the expected ones. assertEquals(7, result.getSpanStart(bulletSpan1).toLong()) assertEquals(11, result.getSpanEnd(bulletSpan1).toLong()) assertEquals(11, result.getSpanStart(bulletSpan2).toLong()) assertEquals(14, result.getSpanEnd(bulletSpan2).toLong()) }
Java
@Test public void textWithBulletPoints() { SpannedString result = builder.markdownToSpans("Points\n* one\n+ two"); // Check whether the markup tags are removed. assertEquals("Points\none\ntwo", result.toString()); // Get all the spans attached to the SpannedString. Object[] spans = result.getSpans(0, result.length(), Object.class); // Check whether the correct number of spans are created. assertEquals(2, spans.length); // Check whether the spans are instances of BulletPointSpan. BulletPointSpan bulletSpan1 = (BulletPointSpan) spans[0]; BulletPointSpan bulletSpan2 = (BulletPointSpan) spans[1]; // Check whether the start and end indices are the expected ones. assertEquals(7, result.getSpanStart(bulletSpan1)); assertEquals(11, result.getSpanEnd(bulletSpan1)); assertEquals(11, result.getSpanStart(bulletSpan2)); assertEquals(14, result.getSpanEnd(bulletSpan2)); }
Weitere Testbeispiele finden Sie unter MarkdownBuilderTest auf GitHub.
Benutzerdefinierte Spans testen
Prüfen Sie beim Testen von Spans, ob TextPaint
die erwarteten
Änderungen vorgenommen wurden und die richtigen Elemente auf deinem Canvas
erscheinen. Für
Stellen Sie sich eine benutzerdefinierte Span-Implementierung vor, bei der
Text. Für den Aufzählungspunkt ist eine bestimmte Größe und Farbe angegeben und es entsteht eine Lücke.
zwischen dem linken Rand des Zeichenbereichs und dem Aufzählungspunkt.
Sie können das Verhalten dieser Klasse testen, indem Sie einen AndroidJUnit-Test implementieren. Überprüfen Sie Folgendes:
- Wenn Sie die Spanne korrekt angewendet haben, können Sie einen Aufzählungspunkt der angegebenen Größe und auf dem Canvas, und der korrekte Abstand zwischen der linken den Rand und den Aufzählungspunkt.
- Wenn Sie den Span nicht anwenden, wird keines der benutzerdefinierten Funktionsweisen angezeigt.
Die Implementierung dieser Tests finden Sie unter TextStyling Beispiel auf GitHub.
Sie können Canvas-Interaktionen testen, indem Sie für den Canvas ein Mockup erstellen,
das Objekt
drawLeadingMargin()
und überprüfen, ob die richtigen Methoden mit der richtigen
Parameter.
Weitere Beispiele für Span-Test finden Sie unter BulletPointSpanTest definiert.
Best Practices für die Verwendung von Spans
Es gibt mehrere speichereffiziente Möglichkeiten, Text in einem TextView
festzulegen, je nachdem,
an Ihre Bedürfnisse anzupassen.
Spans anhängen oder entfernen, ohne den zugrunde liegenden Text zu ändern
TextView.setText()
enthält mehrere Überlastungen, die Spans unterschiedlich verarbeiten. So können Sie zum Beispiel
ein Spannable
-Textobjekt mit dem folgenden Code festlegen:
Kotlin
textView.setText(spannableObject)
Java
textView.setText(spannableObject);
Beim Aufrufen dieser Überlastung von setText()
erstellt TextView
eine Kopie Ihrer
Spannable
als SpannedString
und speichert sie als CharSequence
.
Das bedeutet, dass Ihr Text und die Spans unveränderlich sind.
Aktualisieren Sie den Text oder die Spans, erstellen Sie ein neues Spannable
-Objekt und rufen Sie
setText()
zurück. Dies löst ebenfalls eine erneute Messung und Neuzeichnung des
Layout.
Um anzugeben, dass die Spans änderbar sein müssen, können Sie stattdessen
setText(CharSequence text, TextView.BufferType
type)
,
Dies wird im folgenden Beispiel gezeigt:
Kotlin
textView.setText(spannable, BufferType.SPANNABLE) val spannableText = textView.text as Spannable spannableText.setSpan( ForegroundColorSpan(color), 8, spannableText.length, SPAN_INCLUSIVE_INCLUSIVE )
Java
textView.setText(spannable, BufferType.SPANNABLE); Spannable spannableText = (Spannable) textView.getText(); spannableText.setSpan( new ForegroundColorSpan(color), 8, spannableText.getLength(), SPAN_INCLUSIVE_INCLUSIVE);
In diesem Beispiel hat der Parameter
BufferType.SPANNABLE
erstellt TextView
eine SpannableString
und der Parameter
Das von TextView
beibehaltene CharSequence
-Objekt verfügt jetzt über änderbares Markup und
unveränderlicher Text. Rufen Sie zum Aktualisieren des Spans den Text als Spannable
ab und
Aktualisieren Sie die Spans nach Bedarf.
Wenn Sie Spans verbinden, trennen oder neu positionieren, TextView
um die Textänderung widerzuspiegeln. Wenn Sie ein internes Attribut ändern
eines vorhandenen Bereichs können Sie invalidate()
aufrufen, um
requestLayout()
, um messwertbezogene Änderungen vorzunehmen.
Text in TextView mehrmals festlegen
In einigen Fällen, z. B. bei der Verwendung eines
RecyclerView.ViewHolder
,
können Sie TextView
wiederverwenden und den Text mehrmals festlegen. Von
Unabhängig davon, ob Sie BufferType
festlegen, erstellt die TextView
Eine Kopie des CharSequence
-Objekts und speichert es im Speicher. Damit sind alle
TextView
wird absichtlich aktualisiert. Das Original kann nicht aktualisiert werden.
CharSequence
-Objekts, um den Text zu aktualisieren. Das bedeutet, dass jedes Mal, wenn Sie
Text eingeben, erstellt die TextView
ein neues Objekt.
Wenn Sie diesen Prozess besser steuern und das zusätzliche Objekt vermeiden möchten
können Sie Ihre eigenen
Spannable.Factory
und überschreiben
newSpannable()
Anstatt ein neues Textobjekt zu erstellen, können Sie das vorhandene
CharSequence
als Spannable
, wie im folgenden Beispiel gezeigt:
Kotlin
val spannableFactory = object : Spannable.Factory() { override fun newSpannable(source: CharSequence?): Spannable { return source as Spannable } }
Java
Spannable.Factory spannableFactory = new Spannable.Factory(){ @Override public Spannable newSpannable(CharSequence source) { return (Spannable) source; } };
Sie müssen textView.setText(spannableObject, BufferType.SPANNABLE)
verwenden, wenn
das Festlegen des Textes. Andernfalls wird die Quelle-CharSequence
als Spanned
erstellt.
Instanz und kann nicht in Spannable
umgewandelt werden, wodurch newSpannable()
den Fehler
ClassCastException
Nachdem Sie newSpannable()
überschrieben haben, weisen Sie TextView
an, die neue Factory
zu verwenden:
Kotlin
textView.setSpannableFactory(spannableFactory)
Java
textView.setSpannableFactory(spannableFactory);
Legen Sie das Spannable.Factory
-Objekt einmalig fest, nachdem Sie einen Verweis auf Ihre
TextView
. Wenn Sie ein RecyclerView
verwenden, legen Sie das Factory
-Objekt fest, wenn Sie
die Anzahl der Aufrufe steigern. Dadurch wird die Erstellung zusätzlicher Objekte vermieden,
RecyclerView
bindet ein neues Element an ViewHolder
.
Interne Span-Attribute ändern
Wenn Sie nur ein internes Attribut eines änderbaren Spans ändern müssen, z. B. das
Aufzählungszeichenfarbe in benutzerdefinierten Aufzählungspunkten enthalten, können Sie vermeiden, dass das Aufrufen von
setText()
mehrfach ausführen, indem ein Verweis auf die Span bei der Erstellung beibehalten wird.
Wenn Sie den Span ändern müssen, können Sie den Verweis ändern und dann
invalidate()
oder requestLayout()
auf TextView
, je nach Typ
Attribut, das Sie geändert haben.
Im folgenden Codebeispiel enthält eine benutzerdefinierte Implementierung mit Aufzählungspunkten ein Standardfarbe Rot, die sich beim Antippen einer Schaltfläche in Grau ändert:
Kotlin
class MainActivity : AppCompatActivity() { // Keeping the span as a field. val bulletSpan = BulletPointSpan(color = Color.RED) override fun onCreate(savedInstanceState: Bundle?) { ... val spannable = SpannableString("Text is spantastic") // Setting the span to the bulletSpan field. spannable.setSpan( bulletSpan, 0, 4, Spanned.SPAN_INCLUSIVE_INCLUSIVE ) styledText.setText(spannable) button.setOnClickListener { // Change the color of the mutable span. bulletSpan.color = Color.GRAY // Color doesn't change until invalidate is called. styledText.invalidate() } } }
Java
public class MainActivity extends AppCompatActivity { private BulletPointSpan bulletSpan = new BulletPointSpan(Color.RED); @Override protected void onCreate(Bundle savedInstanceState) { ... SpannableString spannable = new SpannableString("Text is spantastic"); // Setting the span to the bulletSpan field. spannable.setSpan(bulletSpan, 0, 4, Spanned.SPAN_INCLUSIVE_INCLUSIVE); styledText.setText(spannable); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // Change the color of the mutable span. bulletSpan.setColor(Color.GRAY); // Color doesn't change until invalidate is called. styledText.invalidate(); } }); } }
Funktionen der Android KTX-Erweiterung verwenden
Android KTX enthält außerdem Erweiterungsfunktionen, die die Arbeit mit Spans einfacher zu machen. Weitere Informationen finden Sie in der Dokumentation zum androidx.core.text Paket.