Skip to content

Commit

Permalink
Fix Text incorrect line height
Browse files Browse the repository at this point in the history
Summary:
Setting the line height with the help of Android-provided StaticLayout is incorrect. A
simple example app will display the following when `setLineSpacing(50.f, 0.f)`
is set: {F62987699}. You'll notice that the height of the first line is a few
pixels shorter than the other lines.
So we use a custom LineHeightSpan instead, which needs to be applied to the text
itself, and no height-related attributes need to be set on the TextView itself.

Reviewed By: lexs

Differential Revision: D3751097

fbshipit-source-id: c3574a1080efec26436a5c61afbff89afa8679e7
  • Loading branch information
andreicoman11 authored and Facebook Github Bot committed Aug 24, 2016
1 parent e7521a1 commit 483953d
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 35 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2004-present Facebook. All Rights Reserved.

package com.facebook.react.views.text;

import android.graphics.Paint;
import android.text.style.LineHeightSpan;

/**
* We use a custom {@link LineHeightSpan}, because `lineSpacingExtra` is broken. Details here:
* https://github.com/facebook/react-native/issues/7546
*/
public class CustomLineHeightSpan implements LineHeightSpan {
private final float mHeight;

CustomLineHeightSpan(float height) {
this.mHeight = height;
}

@Override
public void chooseHeight(
CharSequence text,
int start,
int end,
int spanstartv,
int v,
Paint.FontMetricsInt fm) {
fm.ascent = fm.top = 0;
fm.descent = fm.bottom = (int) Math.ceil(mHeight);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,12 @@ private static void buildSpannedFromTextCSSNode(
textCSSNode.mTextShadowRadius,
textCSSNode.mTextShadowColor)));
}
if (!Float.isNaN(textCSSNode.getEffectiveLineHeight())) {
ops.add(new SetSpanOperation(
start,
end,
new CustomLineHeightSpan(textCSSNode.getEffectiveLineHeight())));
}
ops.add(new SetSpanOperation(start, end, new ReactTagSpan(textCSSNode.getReactTag())));
}
}
Expand Down Expand Up @@ -235,14 +241,6 @@ public void measure(
// technically, width should never be negative, but there is currently a bug in
boolean unconstrainedWidth = widthMode == CSSMeasureMode.UNDEFINED || width < 0;

float effectiveLineHeight = reactCSSNode.getEffectiveLineHeight();
float lineSpacingExtra = 0;
float lineSpacingMultiplier = 1;
if (!Float.isNaN(effectiveLineHeight)) {
lineSpacingExtra = effectiveLineHeight;
lineSpacingMultiplier = 0;
}

if (boring == null &&
(unconstrainedWidth ||
(!CSSConstants.isUndefined(desiredWidth) && desiredWidth <= width))) {
Expand All @@ -253,8 +251,8 @@ public void measure(
textPaint,
(int) Math.ceil(desiredWidth),
Layout.Alignment.ALIGN_NORMAL,
lineSpacingMultiplier,
lineSpacingExtra,
1.f,
0.f,
true);
} else if (boring != null && (unconstrainedWidth || boring.width <= width)) {
// Is used for single-line, boring text when the width is either unknown or bigger
Expand All @@ -264,8 +262,8 @@ public void measure(
textPaint,
boring.width,
Layout.Alignment.ALIGN_NORMAL,
lineSpacingMultiplier,
lineSpacingExtra,
1.f,
0.f,
boring,
true);
} else {
Expand All @@ -275,8 +273,8 @@ public void measure(
textPaint,
(int) width,
Layout.Alignment.ALIGN_NORMAL,
lineSpacingMultiplier,
lineSpacingExtra,
1.f,
0.f,
true);
}

Expand Down Expand Up @@ -588,7 +586,6 @@ public void onCollectExtraUpdates(UIViewOperationQueue uiViewOperationQueue) {
UNSET,
mContainsImages,
getPadding(),
getEffectiveLineHeight(),
getTextAlign()
);
uiViewOperationQueue.enqueueUpdateExtraData(getReactTag(), reactTextUpdate);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
package com.facebook.react.views.text;

import android.text.Spannable;
import android.view.Gravity;

import com.facebook.csslayout.Spacing;

Expand All @@ -28,15 +27,13 @@ public class ReactTextUpdate {
private final float mPaddingTop;
private final float mPaddingRight;
private final float mPaddingBottom;
private final float mLineHeight;
private final int mTextAlign;

public ReactTextUpdate(
Spannable text,
int jsEventCounter,
boolean containsImages,
Spacing padding,
float lineHeight,
int textAlign) {
mText = text;
mJsEventCounter = jsEventCounter;
Expand All @@ -45,7 +42,6 @@ public ReactTextUpdate(
mPaddingTop = padding.get(Spacing.TOP);
mPaddingRight = padding.get(Spacing.END);
mPaddingBottom = padding.get(Spacing.BOTTOM);
mLineHeight = lineHeight;
mTextAlign = textAlign;
}

Expand Down Expand Up @@ -77,10 +73,6 @@ public float getPaddingBottom() {
return mPaddingBottom;
}

public float getLineHeight() {
return mLineHeight;
}

public int getTextAlign() {
return mTextAlign;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import android.view.ViewGroup;
import android.widget.TextView;

import com.facebook.csslayout.FloatUtil;
import com.facebook.react.uimanager.ReactCompoundView;

public class ReactTextView extends TextView implements ReactCompoundView {
Expand Down Expand Up @@ -54,16 +53,6 @@ public void setText(ReactTextUpdate update) {
(int) Math.ceil(update.getPaddingRight()),
(int) Math.ceil(update.getPaddingBottom()));

float nextLineHeight = update.getLineHeight();
if (!FloatUtil.floatsEqual(mLineHeight, nextLineHeight)) {
mLineHeight = nextLineHeight;
if (Float.isNaN(mLineHeight)) { // NaN will be used if property gets reset
setLineSpacing(0, 1);
} else {
setLineSpacing(mLineHeight, 0);
}
}

int nextTextAlign = update.getTextAlign();
if (mTextAlign != nextTextAlign) {
mTextAlign = nextTextAlign;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,6 @@ public void onCollectExtraUpdates(UIViewOperationQueue uiViewOperationQueue) {
mJsEventCount,
mContainsImages,
getPadding(),
getEffectiveLineHeight(),
mTextAlign
);
uiViewOperationQueue.enqueueUpdateExtraData(getReactTag(), reactTextUpdate);
Expand Down

0 comments on commit 483953d

Please sign in to comment.