Why Text Gets Cut Off in React Native – Android Measurement Deep Dive & Fixes
This article analyzes the long‑standing React Native text‑truncation issue, explains Android’s text‑measurement architecture—including Spanned, TextPaint, Layout and TextLine—examines two common truncation cases, provides concrete fixes, and proposes a runtime monitoring solution to detect and log such problems.
Background
React Native has long suffered from text truncation (also called “phone swallowing characters”) where strings are cut off on certain Android devices.
Text Measurement Principles
Text measurement in Android is performed by a set of core classes: Spanned , TextPaint , Layout and TextLine . Spanned abstracts span data, TextPaint holds paint properties, Layout defines the measurement and drawing process, and TextLine handles per‑line layout.
Key interfaces include CharacterStyle , ParagraphStyle , MetricAffectingSpan , UpdateAppearance , UpdateLayout , etc.
FontMetrics defines the geometric properties of a font.
<code>public static class FontMetrics {
public float top;
public float ascent;
public float descent;
public float bottom;
public float leading;
}</code>Analysis of Truncation Cases
1. fontFamily = null
When the Text component does not specify a fontFamily , React Native creates a CustomStyleSpan with a null typeface. During the YogaMeasure phase the TextPaint has no typeface, producing a narrower width. In the onMeasure phase the TextView applies the system default typeface, resulting in a larger width. The mismatch makes BoringLayout width exceed the wanted width, causing StaticLayout to wrap and truncate the end of the line.
2. System font scaling
Changing the system font scale on MIUI 12/13/14 alters the underlying Misans font metrics, making the width measured in Yoga different from the width measured later, again leading to BoringLayout width > wantWidth and truncation.
Solutions
Ensure the same TextPaint configuration in both phases. For case 1, force the default typeface in YogaMeasure (e.g., reuse TextView#setTypefaceFromAttrs ). For case 2, apply the same font size in onBeforeLayout as in onMeasure , typically via ReactAbsoluteSizeSpan .
A PR has been merged into React Native to address the issue:
Monitoring Scheme
Detect truncation at runtime by comparing the width and line count of the Layout used by Yoga with the Layout after onMeasure :
TextView.mBoring.width > Yoga.mBoring.width
TextView.mLayout.getLineCount() > Yoga.mLayout.getLineCount()
When both conditions hold, log a truncation event.
Conclusion
The article provides a comprehensive overview of Android text measurement, identifies root causes of React Native text truncation, offers concrete fixes, and proposes a lightweight monitoring solution to detect and log such problems.
Kuaishou Frontend Engineering
Explore the cutting‑edge tech behind Kuaishou's front‑end ecosystem
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.