Android TextView Internals: Complex Text Layout Scenarios, Part 4
This is part 4 of the four-part series “Android TextView Internals: Text Measurement and Layout.” In the previous part, we covered line breaking, hyphenation, and alignment.
7. Complex Scenario Handling
Modern apps often need to handle cases that are more complex than simple LTR text.
RTL (Right-to-Left) Text Layout
Android provides strong support for RTL languages such as Arabic and Hebrew.
- Automatic detection:
TextViewandLayoutcan detect whether the text contains RTL characters. - BiDi algorithm: When text mixes LTR and RTL characters, for example Arabic embedded in English, the system applies the Unicode Bidirectional Algorithm to determine the correct display order and direction for each text run.
StaticLayoutandDynamicLayoutimplement BiDi handling internally. - android:textDirection: You can explicitly control the base text direction of a
TextView, commonly with values such aslocale,ltr,rtl, orinherit. ForLayout, this affects howAlignment.ALIGN_NORMALandAlignment.ALIGN_OPPOSITEbehave. For example,ALIGN_NORMALmeans right alignment in an RTL context.

Figure: A TextView containing mixed English and Arabic text, such as “This is an example with Arabic text.” The text should be displayed according to correct BiDi rules: English runs left to right, Arabic runs right to left, while the overall logical order remains correct.
For developers, BiDi handling is usually transparent. The Layout classes are responsible for computing the correct glyph order and positions.
Emoji and Special Characters
- Emoji: Emoji are Unicode characters. Modern Android versions and fonts usually include built-in emoji support.
- Measurement: Emoji are often wider than regular characters, sometimes twice as wide.
Layoutaccounts for their width during measurement and line breaking. - Rendering: The system uses color fonts, such as Noto Color Emoji, to render emoji.
- Line breaking: Emoji are usually treated as a single unit and are not split in the middle.
- Measurement: Emoji are often wider than regular characters, sometimes twice as wide.
- Complex scripts: For languages that require glyph composition or shaping, such as Arabic letter joining or Indic vowel composition, Android uses the HarfBuzz engine. This happens before
Layoutcalculation: text is first shaped by HarfBuzz into the correct glyph sequence and positions, and thenLayoutuses that glyph information for line breaking and placement.
Developers usually do not need to intervene directly in emoji or complex script handling. Use recent Android versions and fonts that support these features.
Rich Text (Spanned String)
TextView supports rich text through the Spanned interface, including different styles, colors, clickable links, and more. When calculating layout, the Layout classes recognize and process the different Span objects inside a Spanned string:
- MetricAffectingSpan: Examples include
StyleSpanfor bold or italic text,RelativeSizeSpan, andTextAppearanceSpan. These spans change text metrics such as width and height, soLayoutmust account for them during measurement and line breaking.BoringLayoutdoes not support this kind of span. - CharacterStyle (non-metric): Examples include
ForegroundColorSpan,BackgroundColorSpan, andUnderlineSpan. These spans only affect drawing, not text metrics, soLayoutmainly applies them during the draw phase. - ParagraphStyle: Examples include
AlignmentSpan,LineBackgroundSpan, andBulletSpan. These spans affect the layout or drawing of an entire line or paragraph.
StaticLayout and DynamicLayout can correctly handle all span types.
SpannableString spannable = new SpannableString("This text has bold, colored, and clickable parts.");
// Bold
spannable.setSpan(new StyleSpan(Typeface.BOLD), 15, 19, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
// Color
spannable.setSpan(new ForegroundColorSpan(Color.RED), 21, 28, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
// Clickable
ClickableSpan clickableSpan = new ClickableSpan() {
@Override
public void onClick(@NonNull View widget) {
Toast.makeText(widget.getContext(), "Clicked!", Toast.LENGTH_SHORT).show();
}
@Override
public void updateDrawState(@NonNull TextPaint ds) {
super.updateDrawState(ds);
ds.setUnderlineText(true); // Make clickable part underlined
ds.setColor(Color.BLUE); // Make clickable part blue
}
};
spannable.setSpan(clickableSpan, 34, 43, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
TextView textView = findViewById(R.id.myTextView);
textView.setText(spannable);
// Important: MovementMethod must be set for ClickableSpan to respond to clicks.
textView.setMovementMethod(LinkMovementMethod.getInstance());
The Layout classes parse these spans, make sure bold text takes the correct width, draw colored text with the correct color, and provide positional information for ClickableSpan so touch events can be handled.
8. Performance Considerations and Optimization Tips
Although TextView is powerful, performance can become a bottleneck when it handles large amounts of text or frequent updates.
- Avoid creating
LayoutorPaintobjects insideonDraw: Creating these objects has a cost. Create or update them during initialization or when text and properties change, then reuse them inonDraw.TextViewalready follows this principle internally. - StaticLayout vs DynamicLayout:
- For text that rarely changes,
StaticLayoutrenders faster. - For editable text, such as
EditText,DynamicLayoutis required. - If your
TextViewcontent updates frequently but is not editable, each update recreates theStaticLayout, which can be expensive. Consider alternatives such as splitting long text withRecyclerView, or updating only changed parts. The latter is often difficult in practice.
- For text that rarely changes,
- Cache
Layoutobjects: If you manually create aLayoutin a custom view and the text plus constraints do not change, cache theLayoutobject to avoid repeated calculation. - TextView optimization:
- Reduce unnecessary
setText()calls: Call it only when the text has actually changed. - Avoid excessive complex spans in lists: Large numbers of complex spans increase layout and drawing cost.
- Consider
PrecomputedText(API 28+): For long text that can be loaded on a background thread, usePrecomputedTextto perform most text layout calculation off the main thread, then set the result on theTextViewon the main thread to reduce UI jank.
- Reduce unnecessary
// On a background thread
TextView textView = findViewById(R.id.myTextView);
CharSequence longText = ... ;
PrecomputedText.Params params = textView.getTextMetricsParams();
Spannable newText = PrecomputedText.create(longText, params);
// Back on the main thread
textView.setText(newText);
- Simplify surrounding layouts: Overly complex
ConstraintLayoutgraphs or deeply nested layouts increase overall measurement and layout time, indirectly affectingTextViewdisplay performance.
9. Debugging Tips
When a TextView does not render as expected, try the following debugging techniques:
- Layout Inspector: Android Studio’s built-in tool lets you inspect the
TextViewbounds, padding, measured width and height, and text content. - Check attributes: Carefully verify XML attributes or code-set values such as
textSize,textColor,lineSpacingExtra,includeFontPadding,breakStrategy,maxLines, andellipsize. - Print FontMetrics: Get the
TextPaintfrom theTextViewand printFontMetricsto understand the current font metrics and baseline position.
TextPaint paint = textView.getPaint();
Paint.FontMetrics fm = paint.getFontMetrics();
Log.d("FontMetrics", "top: " + fm.top + ", ascent: " + fm.ascent +
", descent: " + fm.descent + ", bottom: " + fm.bottom +
", leading: " + fm.leading);
- Simplify the case: Try simpler text, remove spans, use a standard font, remove line spacing, and narrow down which factor causes the issue.
- Visualize bounds: Temporarily set a highly visible background color on the
TextView, or enable Show layout bounds in Developer Options to inspect the actual area occupied by the view.
10. Summary: Mastering TextView Layout
Text measurement and layout in TextView is a precise process involving many factors. Understanding the underlying mechanics, especially the roles and differences of the Layout classes (BoringLayout, StaticLayout, and DynamicLayout) and the impact of font metrics on vertical spacing, is essential for building high-quality, high-performance Android apps.
Key takeaways:
TextView.onMeasurerelies onLayoutobjects to calculate size.BoringLayoutoptimizes simple single-line LTR text.StaticLayoutis the main implementation for complex static multiline text. It is feature-rich but has creation cost.DynamicLayoutis used byEditTextand supports efficient incremental updates.- Line-breaking strategy (
breakStrategy) and hyphenation (hyphenationFrequency) affect text flow. - Font metrics (
top,ascent,descent,bottom) determine vertical space. includeFontPaddingandelegantTextHeightcontrol how font metrics are used to calculate line height and spacing, affecting vertical alignment.- The system automatically handles RTL/BiDi text, emoji, and complex scripts.
Spannedtext enables rich text rendering, withLayouthandling the details.- Performance optimization requires attention to layout choice, caching, and avoiding unnecessary computation.
After this deep dive, you should have a clearer and more complete understanding of how Android TextView measures and lays out text. The next time you tune lineSpacingMultiplier, handle multilingual alignment, or optimize list scrolling performance, you will have a much stronger mental model.
“Android TextView Internals: Text Measurement and Layout” series
- Opening: the journey of text from characters to pixels
- The three major
Layoutimplementations - Line breaking, hyphenation, and alignment
- Complex scenario handling (this article)