Skip to content

Commit 2c1913f

Browse files
janicduplessisfacebook-github-bot
authored andcommitted
Implement adjustsFontSizeToFit on Android (#26389)
Summary: This adds support for `adjustsFontSizeToFit` and `minimumFontScale` on Android. The implementation tries to match closely the behaviour on iOS (hardcoded 4px min size for example). It uses a simpler linear algorithm for now, opened to improving it now if it is a deal breaker or in a follow up. See https://twitter.com/janicduplessis/status/1171147709979516929 for a more detailed thread about the implementation ## Changelog [Android] [Added] - Implement `adjustsFontSizeToFit` on Android Pull Request resolved: #26389 Test Plan: Tested by adding the existing `adjustsFontSizeToFit` example from the iOS text page to android. Also added a case for limiting size by using `maxHeight` instead of `numberOfLines`. Reviewed By: mdvacca Differential Revision: D17285473 Pulled By: JoshuaGross fbshipit-source-id: 43dbdb05e2d6418e9a390d11f921518bfa58e697
1 parent 9f8e4ac commit 2c1913f

File tree

7 files changed

+282
-88
lines changed

7 files changed

+282
-88
lines changed

RNTester/js/examples/Text/TextExample.android.js

+128-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const React = require('react');
1616
const TextInlineView = require('../../components/TextInlineView');
1717
const TextLegend = require('../../components/TextLegend');
1818

19-
const {StyleSheet, Text, View} = require('react-native');
19+
const {LayoutAnimation, StyleSheet, Text, View} = require('react-native');
2020

2121
class Entity extends React.Component<{|children: React.Node|}> {
2222
render() {
@@ -70,10 +70,137 @@ class AttributeToggler extends React.Component<{...}, $FlowFixMeState> {
7070
}
7171
}
7272

73+
type AdjustingFontSizeProps = $ReadOnly<{||}>;
74+
75+
type AdjustingFontSizeState = {|
76+
dynamicText: string,
77+
shouldRender: boolean,
78+
|};
79+
80+
class AdjustingFontSize extends React.Component<
81+
AdjustingFontSizeProps,
82+
AdjustingFontSizeState,
83+
> {
84+
state = {
85+
dynamicText: '',
86+
shouldRender: true,
87+
};
88+
89+
reset = () => {
90+
LayoutAnimation.easeInEaseOut();
91+
this.setState({
92+
shouldRender: false,
93+
});
94+
setTimeout(() => {
95+
LayoutAnimation.easeInEaseOut();
96+
this.setState({
97+
dynamicText: '',
98+
shouldRender: true,
99+
});
100+
}, 300);
101+
};
102+
103+
addText = () => {
104+
this.setState({
105+
dynamicText:
106+
this.state.dynamicText +
107+
(Math.floor((Math.random() * 10) % 2) ? ' foo' : ' bar'),
108+
});
109+
};
110+
111+
removeText = () => {
112+
this.setState({
113+
dynamicText: this.state.dynamicText.slice(
114+
0,
115+
this.state.dynamicText.length - 4,
116+
),
117+
});
118+
};
119+
120+
render() {
121+
if (!this.state.shouldRender) {
122+
return <View />;
123+
}
124+
return (
125+
<View>
126+
<Text
127+
ellipsizeMode="tail"
128+
numberOfLines={1}
129+
style={{fontSize: 36, marginVertical: 6}}>
130+
Truncated text is baaaaad.
131+
</Text>
132+
<Text
133+
numberOfLines={1}
134+
adjustsFontSizeToFit={true}
135+
style={{fontSize: 40, marginVertical: 6}}>
136+
Shrinking to fit available space is much better!
137+
</Text>
138+
139+
<Text
140+
adjustsFontSizeToFit={true}
141+
numberOfLines={1}
142+
style={{fontSize: 30, marginVertical: 6}}>
143+
{'Add text to me to watch me shrink!' + ' ' + this.state.dynamicText}
144+
</Text>
145+
146+
<Text
147+
adjustsFontSizeToFit={true}
148+
numberOfLines={4}
149+
style={{fontSize: 20, marginVertical: 6}}>
150+
{'Multiline text component shrinking is supported, watch as this reeeeaaaally loooooong teeeeeeext grooooows and then shriiiinks as you add text to me! ioahsdia soady auydoa aoisyd aosdy ' +
151+
' ' +
152+
this.state.dynamicText}
153+
</Text>
154+
155+
<Text
156+
adjustsFontSizeToFit={true}
157+
style={{fontSize: 20, marginVertical: 6, maxHeight: 50}}>
158+
{'Text limited by height, watch as this reeeeaaaally loooooong teeeeeeext grooooows and then shriiiinks as you add text to me! ioahsdia soady auydoa aoisyd aosdy ' +
159+
' ' +
160+
this.state.dynamicText}
161+
</Text>
162+
163+
<Text
164+
adjustsFontSizeToFit={true}
165+
numberOfLines={1}
166+
style={{marginVertical: 6}}>
167+
<Text style={{fontSize: 14}}>
168+
{'Differently sized nested elements will shrink together. '}
169+
</Text>
170+
<Text style={{fontSize: 20}}>
171+
{'LARGE TEXT! ' + this.state.dynamicText}
172+
</Text>
173+
</Text>
174+
175+
<View
176+
style={{
177+
flexDirection: 'row',
178+
justifyContent: 'space-around',
179+
marginTop: 5,
180+
marginVertical: 6,
181+
}}>
182+
<Text style={{backgroundColor: '#ffaaaa'}} onPress={this.reset}>
183+
Reset
184+
</Text>
185+
<Text style={{backgroundColor: '#aaaaff'}} onPress={this.removeText}>
186+
Remove Text
187+
</Text>
188+
<Text style={{backgroundColor: '#aaffaa'}} onPress={this.addText}>
189+
Add Text
190+
</Text>
191+
</View>
192+
</View>
193+
);
194+
}
195+
}
196+
73197
class TextExample extends React.Component<{...}> {
74198
render(): React.Node {
75199
return (
76200
<RNTesterPage title="<Text>">
201+
<RNTesterBlock title="Dynamic Font Size Adjustment">
202+
<AdjustingFontSize />
203+
</RNTesterBlock>
77204
<RNTesterBlock title="Wrap">
78205
<Text>
79206
The text should wrap if it goes on multiple lines. See, this is

RNTester/js/examples/Text/TextExample.ios.js

+8
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,14 @@ class AdjustingFontSize extends React.Component<
222222
this.state.dynamicText}
223223
</Text>
224224

225+
<Text
226+
adjustsFontSizeToFit={true}
227+
style={{fontSize: 20, marginVertical: 6, maxHeight: 50}}>
228+
{'Text limited by height, watch as this reeeeaaaally loooooong teeeeeeext grooooows and then shriiiinks as you add text to me! ioahsdia soady auydoa aoisyd aosdy ' +
229+
' ' +
230+
this.state.dynamicText}
231+
</Text>
232+
225233
<Text
226234
adjustsFontSizeToFit={true}
227235
numberOfLines={1}

ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewProps.java

+2
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ public class ViewProps {
9090
public static final String NEEDS_OFFSCREEN_ALPHA_COMPOSITING = "needsOffscreenAlphaCompositing";
9191
public static final String NUMBER_OF_LINES = "numberOfLines";
9292
public static final String ELLIPSIZE_MODE = "ellipsizeMode";
93+
public static final String ADJUSTS_FONT_SIZE_TO_FIT = "adjustsFontSizeToFit";
94+
public static final String MINIMUM_FONT_SCALE = "minimumFontScale";
9395
public static final String ON = "on";
9496
public static final String RESIZE_MODE = "resizeMode";
9597
public static final String RESIZE_METHOD = "resizeMethod";

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

+18-1
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,6 @@ protected Spannable spannedFromShadowNode(
337337
(Build.VERSION.SDK_INT < Build.VERSION_CODES.M) ? 0 : Layout.HYPHENATION_FREQUENCY_NONE;
338338
protected int mJustificationMode =
339339
(Build.VERSION.SDK_INT < Build.VERSION_CODES.O) ? 0 : Layout.JUSTIFICATION_MODE_NONE;
340-
protected TextTransform mTextTransform = TextTransform.UNSET;
341340

342341
protected float mTextShadowOffsetDx = 0;
343342
protected float mTextShadowOffsetDy = 0;
@@ -347,6 +346,8 @@ protected Spannable spannedFromShadowNode(
347346
protected boolean mIsUnderlineTextDecorationSet = false;
348347
protected boolean mIsLineThroughTextDecorationSet = false;
349348
protected boolean mIncludeFontPadding = true;
349+
protected boolean mAdjustsFontSizeToFit = false;
350+
protected float mMinimumFontScale = 0;
350351

351352
/**
352353
* mFontStyle can be {@link Typeface#NORMAL} or {@link Typeface#ITALIC}. mFontWeight can be {@link
@@ -627,4 +628,20 @@ public void setTextTransform(@Nullable String textTransform) {
627628
}
628629
markUpdated();
629630
}
631+
632+
@ReactProp(name = ViewProps.ADJUSTS_FONT_SIZE_TO_FIT)
633+
public void setAdjustFontSizeToFit(boolean adjustsFontSizeToFit) {
634+
if (adjustsFontSizeToFit != mAdjustsFontSizeToFit) {
635+
mAdjustsFontSizeToFit = adjustsFontSizeToFit;
636+
markUpdated();
637+
}
638+
}
639+
640+
@ReactProp(name = ViewProps.MINIMUM_FONT_SCALE)
641+
public void setMinimumFontScale(float minimumFontScale) {
642+
if (minimumFontScale != mMinimumFontScale) {
643+
mMinimumFontScale = minimumFontScale;
644+
markUpdated();
645+
}
646+
}
630647
}

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

+5
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ public void setEllipsizeMode(ReactTextView view, @Nullable String ellipsizeMode)
6060
}
6161
}
6262

63+
@ReactProp(name = ViewProps.ADJUSTS_FONT_SIZE_TO_FIT)
64+
public void setAdjustFontSizeToFit(ReactTextView view, boolean adjustsFontSizeToFit) {
65+
view.setAdjustFontSizeToFit(adjustsFontSizeToFit);
66+
}
67+
6368
@ReactProp(name = ViewProps.TEXT_ALIGN_VERTICAL)
6469
public void setTextAlignVertical(ReactTextView view, @Nullable String textAlignVertical) {
6570
if (textAlignVertical == null || "auto".equals(textAlignVertical)) {

0 commit comments

Comments
 (0)