Skip to content

Commit 9a7ad5b

Browse files
committed
Text correctly aligns with nested inline image. The height of the inline image changes the Text lineHeight
#35704 (comment)
1 parent ba18167 commit 9a7ad5b

File tree

4 files changed

+63
-24
lines changed

4 files changed

+63
-24
lines changed

ReactAndroid/src/main/java/com/facebook/react/views/text/ReactAlignSpan.java

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,28 @@
77

88
package com.facebook.react.views.text;
99

10+
import android.graphics.Rect;
1011
import android.text.TextPaint;
11-
import android.text.style.SuperscriptSpan;
12+
import android.text.style.MetricAffectingSpan;
1213
import android.view.Gravity;
1314
import com.facebook.common.logging.FLog;
1415

1516
/** ratio 0 for center ratio 0.4 for top ratio */
16-
public class ReactAlignSpan extends SuperscriptSpan implements ReactSpan {
17+
public class ReactAlignSpan extends MetricAffectingSpan implements ReactSpan {
1718
private static final String TAG = "ReactAlignSpan";
19+
private final double mLineHeight;
1820
private Integer mParentHeight;
1921
private String mTextAlignVertical;
2022
private Integer mParentGravity;
2123
private int mParentLineCount;
2224
private int mCurrentLine;
2325
private float mCalculatedHeight;
26+
private int mMaximumLineHeight = 0;
27+
private int mOtherSpanLineHeight;
2428

25-
ReactAlignSpan(String textAlignVertical) {
29+
ReactAlignSpan(String textAlignVertical, Float lineHeight) {
2630
mTextAlignVertical = textAlignVertical;
31+
mLineHeight = lineHeight;
2732
}
2833

2934
private double convertTextAlignToStep(String textAlign) {
@@ -87,16 +92,30 @@ public void updateDrawState(TextPaint ds) {
8792
if (numberOfSteps < 0) {
8893
additionalLines = lineHeight * (mParentLineCount - mCurrentLine - 1) * -1;
8994
}
90-
ds.baselineShift -= margin * numberOfSteps + additionalLines;
95+
96+
Rect bounds = new Rect();
97+
ds.getTextBounds("Top", 0, 2, bounds);
98+
// inline image over-riding lineHeight
99+
if (mOtherSpanLineHeight > mLineHeight) {
100+
ds.baselineShift -= mOtherSpanLineHeight - ds.getTextSize();
101+
} else {
102+
ds.baselineShift -= mLineHeight / 2 - ds.getTextSize() / 2;
103+
}
91104
}
92105

93106
public void updateSpan(
94-
Integer height, int gravity, int lineCount, float calculatedHeight, int currentLine) {
107+
Integer height,
108+
int gravity,
109+
int lineCount,
110+
float calculatedHeight,
111+
int currentLine,
112+
int otherSpanLineHeight) {
95113
mParentHeight = height;
96114
mParentGravity = gravity;
97115
mParentLineCount = lineCount;
98116
mCalculatedHeight = calculatedHeight;
99117
mCurrentLine = currentLine;
118+
mOtherSpanLineHeight = otherSpanLineHeight;
100119
}
101120

102121
@Override

ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import java.util.ArrayList;
4646
import java.util.Collections;
4747
import java.util.Comparator;
48+
import java.util.HashMap;
4849

4950
public class ReactTextView extends AppCompatTextView implements ReactCompoundView {
5051

@@ -184,18 +185,42 @@ private ReactContext getReactContext() {
184185
protected void onLayout(
185186
boolean changed, int textViewLeft, int textViewTop, int textViewRight, int textViewBottom) {
186187
// TODO T62882314: Delete this method when Fabric is fully released in OSS
188+
Spanned text = (Spanned) getText();
189+
Layout layout = getLayout();
190+
TextInlineViewPlaceholderSpan[] placeholders =
191+
text.getSpans(0, text.length(), TextInlineViewPlaceholderSpan.class);
192+
HashMap<Integer, Integer> imageLineHeights = new HashMap<Integer, Integer>();
193+
for (TextInlineViewPlaceholderSpan placeholder : placeholders) {
194+
int height = placeholder.getHeight();
195+
int start = text.getSpanStart(placeholder);
196+
int line = layout.getLineForOffset(start);
197+
if (imageLineHeights.get(line) != null && imageLineHeights.get(line) > height) {
198+
imageLineHeights.put(line, height);
199+
}
200+
if (imageLineHeights.get(line) == null) {
201+
imageLineHeights.put(line, height);
202+
}
203+
}
187204
if (getText() instanceof Spanned) {
188-
Spanned text = (Spanned) getText();
189205
ReactAlignSpan[] spans = text.getSpans(0, text.length(), ReactAlignSpan.class);
190-
Layout layout = getLayout();
191206
if (layout != null) {
192207
int lineCount = layout != null ? layout.getLineCount() : 1;
193208
int lineHeight = layout != null ? layout.getHeight() : 0;
194209
if (spans.length != 0) {
195210
for (ReactAlignSpan span : spans) {
196211
int start = text.getSpanStart(span);
197212
int currentLine = layout.getLineForOffset(start);
198-
span.updateSpan(getHeight(), getGravity(), lineCount, layout.getHeight(), currentLine);
213+
int highestLineHeight =
214+
imageLineHeights != null && imageLineHeights.get(currentLine) != null
215+
? imageLineHeights.get(currentLine)
216+
: -1;
217+
span.updateSpan(
218+
getHeight(),
219+
getGravity(),
220+
lineCount,
221+
layout.getHeight(),
222+
currentLine,
223+
highestLineHeight);
199224
}
200225
}
201226
}
@@ -223,8 +248,6 @@ protected void onLayout(
223248
UIManagerModule uiManager =
224249
Assertions.assertNotNull(reactContext.getNativeModule(UIManagerModule.class));
225250

226-
Spanned text = (Spanned) getText();
227-
Layout layout = getLayout();
228251
FLog.w("React::" + TAG, "onLayout " + " layout.getHeight(): " + (layout.getHeight()));
229252
if (layout == null) {
230253
// Text layout is calculated during pre-draw phase, so in some cases it can be empty during
@@ -237,8 +260,6 @@ protected void onLayout(
237260
return;
238261
}
239262

240-
TextInlineViewPlaceholderSpan[] placeholders =
241-
text.getSpans(0, text.length(), TextInlineViewPlaceholderSpan.class);
242263
ArrayList inlineViewInfoArray =
243264
mNotifyOnInlineViewLayout ? new ArrayList(placeholders.length) : null;
244265
int textViewWidth = textViewRight - textViewLeft;

ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManagerMapBuffer.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,11 @@ private static void buildSpannableFromFragment(
145145
|| textAttributes.mVerticalAlign.equals("bottom-child")
146146
|| textAttributes.mVerticalAlign.equals("center-child"))) {
147147
ops.add(
148-
new SetSpanOperation(start, end, new ReactAlignSpan(textAttributes.mVerticalAlign)));
148+
new SetSpanOperation(
149+
start,
150+
end,
151+
new ReactAlignSpan(
152+
textAttributes.mVerticalAlign, textAttributes.getEffectiveLineHeight())));
149153
}
150154
if (textAttributes.mIsAccessibilityLink) {
151155
ops.add(new SetSpanOperation(start, end, new ReactClickableSpan(reactTag)));
@@ -412,11 +416,6 @@ public static long measureText(
412416
? paragraphAttributes.getInt(PA_KEY_MAX_NUMBER_OF_LINES)
413417
: UNSET;
414418

415-
// Calculate the positions of the attachments (views) that will be rendered inside the
416-
// Spanned Text. The following logic is only executed when a text contains views inside.
417-
// This follows a similar logic than used in pre-fabric (see ReactTextView.onLayout method).
418-
// int currentLine = layout.getLineForOffset(start);
419-
420419
int calculatedLineCount =
421420
maximumNumberOfLines == UNSET || maximumNumberOfLines == 0
422421
? layout.getLineCount()

packages/rn-tester/js/examples/Text/TextExample.android.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -211,17 +211,17 @@ class TextExample extends React.Component<{...}> {
211211
return (
212212
<RNTesterPage title="<Text>">
213213
<RNTesterBlock title="Dynamic Font Size Adjustment">
214-
<View style={{height: 250}}>
214+
<View>
215215
<Text
216216
textTransform="uppercase"
217217
style={{
218-
flex: 1,
219-
textAlignVertical: 'center',
218+
textAlignVertical: 'bottom',
220219
backgroundColor: 'yellow',
221220
}}>
222-
A parent text line more in the text a line more in the text a line
221+
A parent text line more in the text a line
223222
<Text
224223
style={{
224+
lineHeight: 100,
225225
textAlignVertical: 'top',
226226
backgroundColor: 'green',
227227
}}>
@@ -232,9 +232,9 @@ class TextExample extends React.Component<{...}> {
232232
style={{
233233
textAlignVertical: 'center',
234234
backgroundColor: 'blue',
235-
color: 'white',
235+
color: 'red',
236236
}}>
237-
Bottom
237+
Center
238238
</Text>
239239
more in the text a line more in the text line more in the
240240
<Text

0 commit comments

Comments
 (0)