Skip to content

Commit df6de4f

Browse files
Abbondanzometa-codesync[bot]
authored andcommitted
Fix TextInput placeholder stuck on multiple lines (#57191)
Summary: Pull Request resolved: #57191 On Android, when a `TextInput`'s `multiline` prop changes from `false` to `true` and back to `false`, the placeholder stayed rendered across multiple lines instead of returning to a single line. `ReactEditText.setInputType` only forced the view out of single-line mode when `multiline` was enabled; it never restored single-line mode when `multiline` was disabled. `TextView.setInputType` re-applies its single-line layout (max lines, horizontal scrolling) only when its internal single-line flag actually changes, and because we toggle that flag off whenever multiline is on, it can be stale so the reset is skipped. On top of that, under the new architecture the view is not re-measured when only the input type changes while the measured size is unchanged. The placeholder is then rebuilt at draw time at the view's physical width and stays wrapped. This change restores single-line mode when `multiline` is turned off (skipping secure-text fields so their password transformation method is preserved) and forces a re-measure on the multiline-to-single-line transition, so the placeholder is laid out on a single line again. Changelog: [Android][Fixed] - Fix `TextInput` placeholder staying on multiple lines after `multiline` is toggled from `true` back to `false` Reviewed By: javache Differential Revision: D108370869 fbshipit-source-id: 3a234723b37aff941cdfc9bbfa9290d033ca65f5
1 parent 58688bf commit df6de4f

1 file changed

Lines changed: 30 additions & 2 deletions

File tree

  • packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.kt

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ public open class ReactEditText public constructor(context: Context) : AppCompat
140140
private var selectTextOnFocus = false
141141
private var placeholder: String? = null
142142
private var overflow = Overflow.VISIBLE
143+
private var wasMultiline = false
143144

144145
public var stateWrapper: StateWrapper? = null
145146
internal var disableTextDiffing: Boolean = false
@@ -544,14 +545,41 @@ public open class ReactEditText public constructor(context: Context) : AppCompat
544545
super.setTypeface(tf)
545546

546547
/**
547-
* If set forces multiline on input, because of a restriction on Android source that enables
548-
* multiline only for inputs of type Text and Multiline on method
548+
* Keep the single-line state in sync with the multiline input type flag.
549+
*
550+
* When multiline is on we must force [isSingleLine] off, because of a restriction on Android
551+
* source that enables multiline only for inputs of type Text and Multiline on method
549552
* [android.widget.TextView.isMultilineInputType]} Source:
550553
* [TextView.java](https://android.googlesource.com/platform/frameworks/base/+/jb-release/core/java/android/widget/TextView.java)
554+
*
555+
* When multiline is off we must force [isSingleLine] back on. [TextView.setInputType] only
556+
* re-applies the single-line layout (maxLines, horizontal scrolling) when its internal
557+
* single-line flag actually changes; because we force it off above whenever multiline is on,
558+
* that flag can be stale and the reset is skipped, leaving the placeholder/hint wrapped across
559+
* multiple lines after multiline is toggled back off. Setting it explicitly guarantees the
560+
* reset. We skip secure text so we don't replace its password transformation method with the
561+
* single-line one.
551562
*/
552563
if (isMultiline) {
553564
isSingleLine = false
565+
} else if (!isSecureText) {
566+
isSingleLine = true
567+
}
568+
569+
// Restoring the single-line input type above is not enough on its own when multiline is toggled
570+
// off: under Fabric the view is not re-measured while its measured size is unchanged, so the
571+
// placeholder/hint is rebuilt at draw time (which lays the hint out at the view's physical
572+
// width) and stays wrapped across multiple lines. Forcing a re-measure at the current bounds
573+
// rebuilds the hint as a single line, matching the initial mount.
574+
if (wasMultiline && !isMultiline && isLaidOut && width > 0 && height > 0) {
575+
forceLayout()
576+
measure(
577+
View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
578+
View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY),
579+
)
580+
layout(left, top, right, bottom)
554581
}
582+
wasMultiline = isMultiline
555583

556584
// We override the KeyListener so that all keys on the soft input keyboard as well as hardware
557585
// keyboards work. Some KeyListeners like DigitsKeyListener will display the keyboard but not

0 commit comments

Comments
 (0)