Skip to content

Commit

Permalink
fix: apply font size to ReactTextView to fix ellipsis cut (#37248)
Browse files Browse the repository at this point in the history
Summary:
This PR aims to fix #36350. In certain cases, when the text is cut due to  `numberOfLines`, the ellipsis appear cut. This is actually an Android bug, which was reported on their side [here](https://issuetracker.google.com/issues/278044456).

This PR contains a workaround for it by applying the text size to the TextView directly instead of just the Spannable inside it. This solves all problems and it seems like it does not cause any regressions.

## Changelog:

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

[ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->

[ANDROID] [FIXED] - Fix ellipsis being cut on certain font sizes

Pull Request resolved: #37248

Test Plan:
One piece of code where the problem could be replicated would be the one below, running on an Pixel 3A emulator.

```jsx
<Text style={{padding: 27, fontSize: 30}} numberOfLines={1}>
   This text will be cut-off strangely in Android
</Text>
```

RN-tester of the problem:

| Before | With the fix  |
| --------------- | --------------- |
|  <img width="460" alt="Screenshot 2023-05-04 at 12 05 11" src="https://user-images.githubusercontent.com/25725586/236187961-d7841594-2d39-4cdc-aff9-a36f60fe6d15.png">| <img width="460" alt="Screenshot 2023-05-04 at 12 08 07" src="https://user-images.githubusercontent.com/25725586/236187999-e823beb5-0473-4940-894e-b3d2ff02c6cc.png"> |

RN-Tester comparison:
| Before | With the fix  |
| --------------- | --------------- |
| <video src="https://user-images.githubusercontent.com/25725586/234273910-c6a9f55c-9a19-415d-b0cd-477c9087dac2.mp4">  | <video src="https://user-images.githubusercontent.com/25725586/234273973-ba6d5bd5-eba8-4eda-aefb-c926ea28c4e5.mp4"> |

Reviewed By: javache

Differential Revision: D45958303

Pulled By: NickGerleman

fbshipit-source-id: 51f77702a82e60c0c18a29ee46b0aba4f37bcc28
  • Loading branch information
BeeMargarida authored and facebook-github-bot committed May 21, 2023
1 parent f8a1f62 commit 6d24ee1
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,16 @@ public void setAdjustFontSizeToFit(ReactTextView view, boolean adjustsFontSizeTo
view.setAdjustFontSizeToFit(adjustsFontSizeToFit);
}

@ReactProp(name = ViewProps.FONT_SIZE)
public void setFontSize(ReactTextView view, float fontSize) {
view.setFontSize(fontSize);
}

@ReactProp(name = ViewProps.LETTER_SPACING, defaultFloat = Float.NaN)
public void setLetterSpacing(ReactTextView view, float letterSpacing) {
view.setLetterSpacing(letterSpacing);
}

@ReactProp(name = ViewProps.TEXT_ALIGN_VERTICAL)
public void setTextAlignVertical(ReactTextView view, @Nullable String textAlignVertical) {
if (textAlignVertical == null || "auto".equals(textAlignVertical)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.text.util.Linkify;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
Expand Down Expand Up @@ -57,6 +58,8 @@ public class ReactTextView extends AppCompatTextView implements ReactCompoundVie
private int mNumberOfLines;
private TextUtils.TruncateAt mEllipsizeLocation;
private boolean mAdjustsFontSizeToFit;
private float mFontSize = Float.NaN;
private float mLetterSpacing = Float.NaN;
private int mLinkifyMaskType;
private boolean mNotifyOnInlineViewLayout;
private boolean mTextIsSelectable;
Expand Down Expand Up @@ -582,6 +585,29 @@ public void setAdjustFontSizeToFit(boolean adjustsFontSizeToFit) {
mAdjustsFontSizeToFit = adjustsFontSizeToFit;
}

public void setFontSize(float fontSize) {
mFontSize =
mAdjustsFontSizeToFit
? (float) Math.ceil(PixelUtil.toPixelFromSP(fontSize))
: (float) Math.ceil(PixelUtil.toPixelFromDIP(fontSize));

applyTextAttributes();
}

public void setLetterSpacing(float letterSpacing) {
if (Float.isNaN(letterSpacing)) {
return;
}

float letterSpacingPixels = PixelUtil.toPixelFromDIP(letterSpacing);

// `letterSpacingPixels` and `getEffectiveFontSize` are both in pixels,
// yielding an accurate em value.
mLetterSpacing = letterSpacingPixels / mFontSize;

applyTextAttributes();
}

public void setEllipsizeLocation(TextUtils.TruncateAt ellipsizeLocation) {
mEllipsizeLocation = ellipsizeLocation;
}
Expand Down Expand Up @@ -651,4 +677,17 @@ protected boolean dispatchHoverEvent(MotionEvent event) {

return super.dispatchHoverEvent(event);
}

private void applyTextAttributes() {
// Workaround for an issue where text can be cut off with an ellipsis when
// using certain font sizes and padding. Sets the provided text size and
// letter spacing to ensure consistent rendering and prevent cut-off.
if (!Float.isNaN(mFontSize)) {
setTextSize(TypedValue.COMPLEX_UNIT_PX, mFontSize);
}

if (!Float.isNaN(mLetterSpacing)) {
super.setLetterSpacing(mLetterSpacing);
}
}
}
4 changes: 4 additions & 0 deletions packages/rn-tester/js/examples/Text/TextExample.android.js
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,10 @@ class TextExample extends React.Component<{...}> {
Maximum of one line no matter now much I write here. If I keep
writing it{"'"}ll just truncate after one line
</Text>
<Text style={{fontSize: 31}} numberOfLines={1}>
Maximum of one line no matter now much I write here. If I keep
writing it{"'"}ll just truncate after one line
</Text>
<Text numberOfLines={2} style={{marginTop: 20}}>
Maximum of two lines no matter now much I write here. If I keep
writing it{"'"}ll just truncate after two lines
Expand Down

0 comments on commit 6d24ee1

Please sign in to comment.