From ccbbcaab9cf3e6148e72f94fe63f77ce5f92416c Mon Sep 17 00:00:00 2001 From: Grisha Pushkov Date: Tue, 14 Mar 2023 12:24:02 -0700 Subject: [PATCH] Fix layout width calculation in onTextLayout (#36222) Summary: There is a rounding issue in layout calculation when using onTextLayout method in Text component on Android. As you can see in the example below onTextLayout returns 3 lines, but in fact text is rendered in 2 lines: Screenshot 2023-02-19 at 23 48 53 This happens because `(int) width` casting works like `Math.floor`, but after changing it to `Math.round` we get correct result: Screenshot 2023-02-19 at 23 51 11 ## Changelog [ANDROID] [FIXED] - Fix layout width calculation in onTextLayout Pull Request resolved: https://github.com/facebook/react-native/pull/36222 Test Plan: This issue can be tricky to reproduce as width calculation depends on device width. I'm attaching code that I used to reproduce it. You need to tap on the screen to run through different sentences and sooner or later you will get the one with this rounding issue.
Code to reproduce ```js import React, { useState, useEffect } from 'react' import { SafeAreaView, Text, View } from 'react-native' function App() { const [state, setState] = useState({ index: 0, lines: [], sentences: [], }) const onTextLayout = (event) => { const lines = event.nativeEvent.lines console.log(JSON.stringify(lines, null, 2)) setState(state => ({ ...state, lines })) } useEffect(() => { fetch('https://content.duoreading.io/20-the-adventures-of-tom-sawyer/translations/english.json') .then(response => response.text()) .then(response => { setState(state => ({ ...state, sentences: JSON.parse(response) })) }) }, []) return ( setState(state => ({ ...state, index: state.index + 1 }))}> {state.sentences[state.index]} {state.lines.map((line, index) => ( ))} ) } export default App ```
Reviewed By: christophpurrer Differential Revision: D43907184 Pulled By: javache fbshipit-source-id: faef757e77e759b5d9ea26da21c9e2b396dc9ff1 --- .../com/facebook/react/views/text/ReactTextShadowNode.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java index 4e67070aca4bd0..2e237f01970fb5 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java @@ -263,6 +263,13 @@ private Layout measureSpannedText(Spannable text, float width, YogaMeasureMode w new StaticLayout( text, textPaint, (int) width, alignment, 1.f, 0.f, mIncludeFontPadding); } else { + // Android 11+ introduces changes in text width calculation which leads to cases + // where the container is measured smaller than text. Math.ceil prevents it + // See T136756103 for investigation + if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.Q) { + width = (float) Math.ceil(width); + } + StaticLayout.Builder builder = StaticLayout.Builder.obtain(text, 0, text.length(), textPaint, (int) width) .setAlignment(alignment)