This code is duplicated in ReactTextInputManager TODO: Factor into a common place they can - * both use - */ - private static int parseNumericFontWeight(String fontWeightString) { - // This should be much faster than using regex to verify input and Integer.parseInt - return fontWeightString.length() == 3 - && fontWeightString.endsWith("00") - && fontWeightString.charAt(0) <= '9' - && fontWeightString.charAt(0) >= '1' - ? 100 * (fontWeightString.charAt(0) - '0') - : UNSET; - } - protected TextAttributes mTextAttributes; protected boolean mIsColorSet = false; @@ -490,37 +473,18 @@ public abstract class ReactBaseTextShadowNode extends LayoutShadowNode { markUpdated(); } - /** - * /* This code is duplicated in ReactTextInputManager /* TODO: Factor into a common place they - * can both use - */ @ReactProp(name = ViewProps.FONT_WEIGHT) public void setFontWeight(@Nullable String fontWeightString) { - int fontWeightNumeric = - fontWeightString != null ? parseNumericFontWeight(fontWeightString) : UNSET; - int fontWeight = fontWeightNumeric != UNSET ? fontWeightNumeric : Typeface.NORMAL; - - if (fontWeight == 700 || "bold".equals(fontWeightString)) fontWeight = Typeface.BOLD; - else if (fontWeight == 400 || "normal".equals(fontWeightString)) fontWeight = Typeface.NORMAL; - + int fontWeight = ReactTypefaceUtils.parseFontWeight(fontWeightString); if (fontWeight != mFontWeight) { mFontWeight = fontWeight; markUpdated(); } } - /** - * /* This code is duplicated in ReactTextInputManager /* TODO: Factor into a common place they - * can both use - */ @ReactProp(name = ViewProps.FONT_STYLE) public void setFontStyle(@Nullable String fontStyleString) { - int fontStyle = UNSET; - if ("italic".equals(fontStyleString)) { - fontStyle = Typeface.ITALIC; - } else if ("normal".equals(fontStyleString)) { - fontStyle = Typeface.NORMAL; - } + int fontStyle = ReactTypefaceUtils.parseFontStyle(fontStyleString); if (fontStyle != mFontStyle) { mFontStyle = fontStyle; markUpdated(); diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTypefaceUtils.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTypefaceUtils.java new file mode 100644 index 00000000000..af1696bb06c --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTypefaceUtils.java @@ -0,0 +1,87 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + *
This source code is licensed under the MIT license found in the LICENSE file in the root
+ * directory of this source tree.
+ */
+package com.facebook.react.views.text;
+
+import android.content.res.AssetManager;
+import android.graphics.Paint;
+import android.graphics.Typeface;
+
+import androidx.annotation.Nullable;
+
+public class ReactTypefaceUtils {
+ public static final int UNSET = -1;
+
+ public static int parseFontWeight(@Nullable String fontWeightString) {
+ int fontWeightNumeric =
+ fontWeightString != null ? parseNumericFontWeight(fontWeightString) : UNSET;
+ int fontWeight = fontWeightNumeric != UNSET ? fontWeightNumeric : Typeface.NORMAL;
+
+ if (fontWeight == 700 || "bold".equals(fontWeightString)) fontWeight = Typeface.BOLD;
+ else if (fontWeight == 400 || "normal".equals(fontWeightString)) fontWeight = Typeface.NORMAL;
+
+ return fontWeight;
+ }
+
+ public static int parseFontStyle(@Nullable String fontStyleString) {
+ int fontStyle = UNSET;
+ if ("italic".equals(fontStyleString)) {
+ fontStyle = Typeface.ITALIC;
+ } else if ("normal".equals(fontStyleString)) {
+ fontStyle = Typeface.NORMAL;
+ }
+
+ return fontStyle;
+ }
+
+ public static Typeface applyStyles(@Nullable Typeface typeface,
+ int style, int weight, @Nullable String family, AssetManager assetManager) {
+ int oldStyle;
+ if (typeface == null) {
+ oldStyle = 0;
+ } else {
+ oldStyle = typeface.getStyle();
+ }
+
+ int want = 0;
+ if ((weight == Typeface.BOLD)
+ || ((oldStyle & Typeface.BOLD) != 0 && weight == ReactTextShadowNode.UNSET)) {
+ want |= Typeface.BOLD;
+ }
+
+ if ((style == Typeface.ITALIC)
+ || ((oldStyle & Typeface.ITALIC) != 0 && style == ReactTextShadowNode.UNSET)) {
+ want |= Typeface.ITALIC;
+ }
+
+ if (family != null) {
+ typeface = ReactFontManager.getInstance().getTypeface(family, want, weight, assetManager);
+ } else if (typeface != null) {
+ // TODO(t9055065): Fix custom fonts getting applied to text children with different style
+ typeface = Typeface.create(typeface, want);
+ }
+
+ if (typeface != null) {
+ return typeface;
+ } else {
+ return Typeface.defaultFromStyle(want);
+ }
+ }
+
+ /**
+ * Return -1 if the input string is not a valid numeric fontWeight (100, 200, ..., 900), otherwise
+ * return the weight.
+ */
+ private static int parseNumericFontWeight(String fontWeightString) {
+ // This should be much faster than using regex to verify input and Integer.parseInt
+ return fontWeightString.length() == 3
+ && fontWeightString.endsWith("00")
+ && fontWeightString.charAt(0) <= '9'
+ && fontWeightString.charAt(0) >= '1'
+ ? 100 * (fontWeightString.charAt(0) - '0')
+ : UNSET;
+ }
+}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java
index 46487c48134..02f4a0a061b 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java
@@ -38,6 +38,7 @@ import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.views.text.ReactSpan;
import com.facebook.react.views.text.ReactTextUpdate;
+import com.facebook.react.views.text.ReactTypefaceUtils;
import com.facebook.react.views.text.TextAttributes;
import com.facebook.react.views.text.TextInlineImageSpan;
import com.facebook.react.views.view.ReactViewBackgroundManager;
@@ -83,6 +84,10 @@ public class ReactEditText extends EditText {
private boolean mDetectScrollMovement = false;
private boolean mOnKeyPress = false;
private TextAttributes mTextAttributes;
+ private boolean mTypefaceDirty = false;
+ private @Nullable String mFontFamily = null;
+ private int mFontWeight = ReactTypefaceUtils.UNSET;
+ private int mFontStyle = ReactTypefaceUtils.UNSET;
private ReactViewBackgroundManager mReactBackgroundManager;
@@ -382,6 +387,39 @@ public class ReactEditText extends EditText {
setKeyListener(mKeyListener);
}
+ public void setFontFamily(String fontFamily) {
+ mFontFamily = fontFamily;
+ mTypefaceDirty = true;
+ }
+
+ public void setFontWeight(String fontWeightString) {
+ int fontWeight = ReactTypefaceUtils.parseFontWeight(fontWeightString);
+ if (fontWeight != mFontWeight) {
+ mFontWeight = fontWeight;
+ mTypefaceDirty = true;
+ }
+ }
+
+ public void setFontStyle(String fontStyleString) {
+ int fontStyle = ReactTypefaceUtils.parseFontStyle(fontStyleString);
+ if (fontStyle != mFontStyle) {
+ mFontStyle = fontStyle;
+ mTypefaceDirty = true;
+ }
+ }
+
+ public void maybeUpdateTypeface() {
+ if (!mTypefaceDirty) {
+ return;
+ }
+
+ mTypefaceDirty = false;
+
+ Typeface newTypeface = ReactTypefaceUtils.applyStyles(
+ getTypeface(), mFontStyle, mFontWeight, mFontFamily, getContext().getAssets());
+ setTypeface(newTypeface);
+ }
+
// VisibleForTesting from {@link TextInputEventsTestCase}.
public void requestFocusFromJS() {
mShouldAllowFocus = true;
diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java
index 8c9e3209583..5dfc945ca91 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java
@@ -230,14 +230,7 @@ public class ReactTextInputManager extends BaseViewManager