Fix Android onTextLayout reporting extra lines when width is fractional#56322
Fix Android onTextLayout reporting extra lines when width is fractional#56322dexical30 wants to merge 1 commit intofacebook:mainfrom
Conversation
…ndroid `measureLines()` passes `YogaMeasureMode.EXACTLY` to `createLayout()`. In the pre-Android 15 path, `EXACTLY` mode used `floor(width)` to compute the StaticLayout width, which could produce a layout that is 1px narrower than the width allocated by Yoga. This causes single-line text (e.g. "Welcome!") to be reported as multiple lines in `onTextLayout` when placed next to a `flexGrow: 1` sibling — even though the text visually renders on one line. Changing `floor` to `ceil` ensures the StaticLayout has at least enough space to match what the TextView actually renders. Fixes: facebook#54552
|
Hi @dexical30! Thank you for your pull request and welcome to our community. Action RequiredIn order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you. ProcessIn order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA. Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks! |
|
Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks! |
Hi, I found a bug while investigating
onTextLayoutbehavior on Android and would love any feedback on the approach.onTextLayoutincorrectly reports a single visual line as multiple lines on Android when<Text>is placed next to aflexGrow: 1sibling.What I Found
After digging into the code, I believe the root cause is in
TextLayoutManager.kt. WhenonTextLayoutis present,measureLines()is called (via JNI fromFabricUIManager) and creates a separateStaticLayoutpurely for measurement — independent of the oneTextViewuses for rendering.In the pre-Android 15 path,
EXACTLYmode computes theStaticLayoutwidth usingfloor(width):When
width(the Yoga-allocated pixel value) has a fractional part — which is common on non-integer density devices like Pixel 4 (density=2.75) or Galaxy series (density=3.5) —floor()produces aStaticLayoutthat is 1px narrower than whatTextViewactually renders with.This causes the two
StaticLayouts to disagree on line breaks:The
flexGrow: 1sibling seems to amplify this because Yoga's flex distribution appliesMath.round()when allocating pixel widths, which more frequently produces fractional remainders that trigger this rounding discrepancy.Proposed Fix
Changing
floortoceilforEXACTLYmode in the pre-Android 15 path:I believe this aligns the
measureLinesStaticLayoutwidth with the actual rendered width. Please let me know if there's a better approach or if I'm missing something.iOS Comparison
While investigating, I noticed that the iOS equivalent (
RCTTextLayoutManager.mm) has always usedceil():iOS also uses
CGFloatthroughout the layout pipeline without integer pixel conversion, so this class of rounding issue doesn't occur there. This change seemed to bring Android's behavior in line with iOS.Changelog:
[ANDROID] [FIXED] - Fix
onTextLayoutreporting incorrect line splits for single-line text in flex layoutsTest Plan:
Setup: On Android, render a
<Text>withonTextLayoutnext to aflexGrow: 1sibling in a row, and log (or display) thelinespayload.Steps:
Before fix: The string visually fits on one line, but onTextLayout reports two lines with text split like "Welcome" and "!".
After fix: lines matches what is rendered: one line whose text is the full "Welcome!".
Related issues for context: