RN: Unify Typeface Logic (Android)
Summary: Refactors how `Typeface` style and weight are applied in React Native on Android. - Unifies all style and weight normalization logic into a new `TypefaceStyle` class. - Fixes font weight support for the Fabric renderer. - De-duplicates code with `TextAttributeProps`. - Simplified normalization logic. - Fixes a rare crash due to `Typeface.sDefaultTypeface` (Android SDK) being `null`. - Adds a new example to test font weights in `TextInput`. - Adds missing `Nullsafe` and `Nullable` annotations. - Clean up a bunch of obsolete inline comments. Changelog: [Android][Fixed] - Fixed a rare crash due to `Typeface.sDefaultTypeface` (Android SDK) being `null`. [Android][Fixed] - Fixed font weight support for the Fabric renderer. [Android][Added] - Added a new example to test font weights in `TextInput`. Reviewed By: JoshuaGross Differential Revision: D29631134 fbshipit-source-id: 3f227d84253104fa828a5561b77ba7a9cbc030c4
This commit is contained in:
Родитель
3e2bb331fc
Коммит
9d2fedc6e2
|
@ -12,9 +12,10 @@ import android.graphics.Paint;
|
|||
import android.graphics.Typeface;
|
||||
import android.text.TextPaint;
|
||||
import android.text.style.MetricAffectingSpan;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.infer.annotation.Nullsafe;
|
||||
|
||||
@Nullsafe(Nullsafe.Mode.LOCAL)
|
||||
public class CustomStyleSpan extends MetricAffectingSpan implements ReactSpan {
|
||||
|
||||
/**
|
||||
|
@ -40,7 +41,7 @@ public class CustomStyleSpan extends MetricAffectingSpan implements ReactSpan {
|
|||
int fontWeight,
|
||||
@Nullable String fontFeatureSettings,
|
||||
@Nullable String fontFamily,
|
||||
@NonNull AssetManager assetManager) {
|
||||
AssetManager assetManager) {
|
||||
mStyle = fontStyle;
|
||||
mWeight = fontWeight;
|
||||
mFeatureSettings = fontFeatureSettings;
|
||||
|
@ -54,21 +55,18 @@ public class CustomStyleSpan extends MetricAffectingSpan implements ReactSpan {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateMeasureState(@NonNull TextPaint paint) {
|
||||
public void updateMeasureState(TextPaint paint) {
|
||||
apply(paint, mStyle, mWeight, mFeatureSettings, mFontFamily, mAssetManager);
|
||||
}
|
||||
|
||||
/** Returns {@link Typeface#NORMAL} or {@link Typeface#ITALIC}. */
|
||||
public int getStyle() {
|
||||
return (mStyle == ReactTextShadowNode.UNSET ? 0 : mStyle);
|
||||
return mStyle == ReactBaseTextShadowNode.UNSET ? Typeface.NORMAL : mStyle;
|
||||
}
|
||||
|
||||
/** Returns {@link Typeface#NORMAL} or {@link Typeface#BOLD}. */
|
||||
public int getWeight() {
|
||||
return (mWeight == ReactTextShadowNode.UNSET ? 0 : mWeight);
|
||||
return mWeight == ReactBaseTextShadowNode.UNSET ? TypefaceStyle.NORMAL : mWeight;
|
||||
}
|
||||
|
||||
/** Returns the font family set for this StyleSpan. */
|
||||
public @Nullable String getFontFamily() {
|
||||
return mFontFamily;
|
||||
}
|
||||
|
|
|
@ -10,31 +10,39 @@ package com.facebook.react.views.text;
|
|||
import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Build;
|
||||
import android.util.SparseArray;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
import com.facebook.infer.annotation.Nullsafe;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Class responsible to load and cache Typeface objects. It will first try to load typefaces inside
|
||||
* the assets/fonts folder and if it doesn't find the right Typeface in that folder will fall back
|
||||
* on the best matching system Typeface The supported custom fonts extensions are .ttf and .otf. For
|
||||
* each font family the bold, italic and bold_italic variants are supported. Given a "family" font
|
||||
* family the files in the assets/fonts folder need to be family.ttf(.otf) family_bold.ttf(.otf)
|
||||
* family_italic.ttf(.otf) and family_bold_italic.ttf(.otf)
|
||||
* Responsible for loading and caching Typeface objects.
|
||||
*
|
||||
* <p>This will first try to load a typeface from the assets/fonts folder. If one is not found in
|
||||
* that folder, this will fallback to the best matching system typeface.
|
||||
*
|
||||
* <p>Custom fonts support the extensions `.ttf` and `.otf` and the variants `bold`, `italic`, and
|
||||
* `bold_italic`. For example, given a font named "ExampleFontFamily", the following are supported:
|
||||
*
|
||||
* <ul>
|
||||
* <li>ExampleFontFamily.ttf (or .otf)
|
||||
* <li>ExampleFontFamily_bold.ttf (or .otf)
|
||||
* <li>ExampleFontFamily_italic.ttf (or .otf)
|
||||
* <li>ExampleFontFamily_bold_italic.ttf (or .otf)
|
||||
*/
|
||||
@Nullsafe(Nullsafe.Mode.LOCAL)
|
||||
public class ReactFontManager {
|
||||
|
||||
// NOTE: Indices in `EXTENSIONS` correspond to the `TypeFace` style constants.
|
||||
private static final String[] EXTENSIONS = {"", "_bold", "_italic", "_bold_italic"};
|
||||
private static final String[] FILE_EXTENSIONS = {".ttf", ".otf"};
|
||||
private static final String FONTS_ASSET_PATH = "fonts/";
|
||||
|
||||
private static ReactFontManager sReactFontManagerInstance;
|
||||
|
||||
private final Map<String, FontFamily> mFontCache;
|
||||
private final Map<String, AssetFontFamily> mFontCache;
|
||||
private final Map<String, Typeface> mCustomTypefaceCache;
|
||||
|
||||
private ReactFontManager() {
|
||||
|
@ -49,36 +57,43 @@ public class ReactFontManager {
|
|||
return sReactFontManagerInstance;
|
||||
}
|
||||
|
||||
public @Nullable Typeface getTypeface(
|
||||
String fontFamilyName, int style, AssetManager assetManager) {
|
||||
return getTypeface(fontFamilyName, style, 0, assetManager);
|
||||
public Typeface getTypeface(String fontFamilyName, int style, AssetManager assetManager) {
|
||||
return getTypeface(fontFamilyName, new TypefaceStyle(style), assetManager);
|
||||
}
|
||||
|
||||
public @Nullable Typeface getTypeface(
|
||||
public Typeface getTypeface(
|
||||
String fontFamilyName, int weight, boolean italic, AssetManager assetManager) {
|
||||
return getTypeface(fontFamilyName, new TypefaceStyle(weight, italic), assetManager);
|
||||
}
|
||||
|
||||
public Typeface getTypeface(
|
||||
String fontFamilyName, int style, int weight, AssetManager assetManager) {
|
||||
return getTypeface(fontFamilyName, new TypefaceStyle(style, weight), assetManager);
|
||||
}
|
||||
|
||||
public Typeface getTypeface(
|
||||
String fontFamilyName, TypefaceStyle typefaceStyle, AssetManager assetManager) {
|
||||
if (mCustomTypefaceCache.containsKey(fontFamilyName)) {
|
||||
Typeface typeface = mCustomTypefaceCache.get(fontFamilyName);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && weight >= 100 && weight <= 1000) {
|
||||
return Typeface.create(typeface, weight, (style & Typeface.ITALIC) != 0);
|
||||
}
|
||||
return Typeface.create(typeface, style);
|
||||
// Apply `typefaceStyle` because custom fonts configure variants using `app:fontStyle` and
|
||||
// `app:fontWeight` in their resource XML configuration file.
|
||||
return typefaceStyle.apply(mCustomTypefaceCache.get(fontFamilyName));
|
||||
}
|
||||
|
||||
FontFamily fontFamily = mFontCache.get(fontFamilyName);
|
||||
if (fontFamily == null) {
|
||||
fontFamily = new FontFamily();
|
||||
mFontCache.put(fontFamilyName, fontFamily);
|
||||
AssetFontFamily assetFontFamily = mFontCache.get(fontFamilyName);
|
||||
if (assetFontFamily == null) {
|
||||
assetFontFamily = new AssetFontFamily();
|
||||
mFontCache.put(fontFamilyName, assetFontFamily);
|
||||
}
|
||||
|
||||
Typeface typeface = fontFamily.getTypeface(style);
|
||||
if (typeface == null) {
|
||||
typeface = createTypeface(fontFamilyName, style, assetManager);
|
||||
if (typeface != null) {
|
||||
fontFamily.setTypeface(style, typeface);
|
||||
}
|
||||
}
|
||||
int style = typefaceStyle.getNearestStyle();
|
||||
|
||||
return typeface;
|
||||
Typeface assetTypeface = assetFontFamily.getTypefaceForStyle(style);
|
||||
if (assetTypeface == null) {
|
||||
assetTypeface = createAssetTypeface(fontFamilyName, style, assetManager);
|
||||
assetFontFamily.setTypefaceForStyle(style, assetTypeface);
|
||||
}
|
||||
// Do not apply `typefaceStyle` because asset font files already incorporate the style.
|
||||
return assetTypeface;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -88,7 +103,7 @@ public class ReactFontManager {
|
|||
*
|
||||
* ReactFontManager.getInstance().addCustomFont(this, "Srisakdi", R.font.srisakdi);
|
||||
*/
|
||||
public void addCustomFont(@NonNull Context context, @NonNull String fontFamily, int fontId) {
|
||||
public void addCustomFont(Context context, String fontFamily, int fontId) {
|
||||
Typeface font = ResourcesCompat.getFont(context, fontId);
|
||||
if (font != null) {
|
||||
mCustomTypefaceCache.put(fontFamily, font);
|
||||
|
@ -106,16 +121,16 @@ public class ReactFontManager {
|
|||
*/
|
||||
public void setTypeface(String fontFamilyName, int style, Typeface typeface) {
|
||||
if (typeface != null) {
|
||||
FontFamily fontFamily = mFontCache.get(fontFamilyName);
|
||||
if (fontFamily == null) {
|
||||
fontFamily = new FontFamily();
|
||||
mFontCache.put(fontFamilyName, fontFamily);
|
||||
AssetFontFamily assetFontFamily = mFontCache.get(fontFamilyName);
|
||||
if (assetFontFamily == null) {
|
||||
assetFontFamily = new AssetFontFamily();
|
||||
mFontCache.put(fontFamilyName, assetFontFamily);
|
||||
}
|
||||
fontFamily.setTypeface(style, typeface);
|
||||
assetFontFamily.setTypefaceForStyle(style, typeface);
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable Typeface createTypeface(
|
||||
private static Typeface createAssetTypeface(
|
||||
String fontFamilyName, int style, AssetManager assetManager) {
|
||||
String extension = EXTENSIONS[style];
|
||||
for (String fileExtension : FILE_EXTENSIONS) {
|
||||
|
@ -129,27 +144,27 @@ public class ReactFontManager {
|
|||
try {
|
||||
return Typeface.createFromAsset(assetManager, fileName);
|
||||
} catch (RuntimeException e) {
|
||||
// unfortunately Typeface.createFromAsset throws an exception instead of returning null
|
||||
// if the typeface doesn't exist
|
||||
// If the typeface asset does not exist, try another extension.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return Typeface.create(fontFamilyName, style);
|
||||
}
|
||||
|
||||
private static class FontFamily {
|
||||
/** Responsible for caching typefaces for each custom font family. */
|
||||
private static class AssetFontFamily {
|
||||
|
||||
private SparseArray<Typeface> mTypefaceSparseArray;
|
||||
|
||||
private FontFamily() {
|
||||
private AssetFontFamily() {
|
||||
mTypefaceSparseArray = new SparseArray<>(4);
|
||||
}
|
||||
|
||||
public Typeface getTypeface(int style) {
|
||||
public @Nullable Typeface getTypefaceForStyle(int style) {
|
||||
return mTypefaceSparseArray.get(style);
|
||||
}
|
||||
|
||||
public void setTypeface(int style, Typeface typeface) {
|
||||
public void setTypefaceForStyle(int style, Typeface typeface) {
|
||||
mTypefaceSparseArray.put(style, typeface);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,38 +9,54 @@ package com.facebook.react.views.text;
|
|||
|
||||
import android.content.res.AssetManager;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.infer.annotation.Nullsafe;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Nullsafe(Nullsafe.Mode.LOCAL)
|
||||
public class ReactTypefaceUtils {
|
||||
private static final String TAG = "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 ("bold".equals(fontWeightString)) fontWeight = Typeface.BOLD;
|
||||
else if ("normal".equals(fontWeightString)) fontWeight = Typeface.NORMAL;
|
||||
|
||||
return fontWeight;
|
||||
if (fontWeightString != null) {
|
||||
switch (fontWeightString) {
|
||||
case "100":
|
||||
return 100;
|
||||
case "200":
|
||||
return 200;
|
||||
case "300":
|
||||
return 300;
|
||||
case "normal":
|
||||
case "400":
|
||||
return 400;
|
||||
case "500":
|
||||
return 500;
|
||||
case "600":
|
||||
return 600;
|
||||
case "bold":
|
||||
case "700":
|
||||
return 700;
|
||||
case "800":
|
||||
return 800;
|
||||
case "900":
|
||||
return 900;
|
||||
}
|
||||
}
|
||||
return ReactBaseTextShadowNode.UNSET;
|
||||
}
|
||||
|
||||
public static int parseFontStyle(@Nullable String fontStyleString) {
|
||||
int fontStyle = UNSET;
|
||||
if (fontStyleString != null) {
|
||||
if ("italic".equals(fontStyleString)) {
|
||||
fontStyle = Typeface.ITALIC;
|
||||
} else if ("normal".equals(fontStyleString)) {
|
||||
fontStyle = Typeface.NORMAL;
|
||||
return Typeface.ITALIC;
|
||||
}
|
||||
|
||||
return fontStyle;
|
||||
if ("normal".equals(fontStyleString)) {
|
||||
return Typeface.NORMAL;
|
||||
}
|
||||
}
|
||||
return ReactBaseTextShadowNode.UNSET;
|
||||
}
|
||||
|
||||
public static @Nullable String parseFontVariant(@Nullable ReadableArray fontVariantArray) {
|
||||
|
@ -80,67 +96,14 @@ public class ReactTypefaceUtils {
|
|||
@Nullable Typeface typeface,
|
||||
int style,
|
||||
int weight,
|
||||
@Nullable String family,
|
||||
@Nullable String fontFamilyName,
|
||||
AssetManager assetManager) {
|
||||
int oldStyle;
|
||||
if (typeface == null) {
|
||||
oldStyle = Typeface.NORMAL;
|
||||
TypefaceStyle typefaceStyle = new TypefaceStyle(style, weight);
|
||||
if (fontFamilyName == null) {
|
||||
return typefaceStyle.apply(typeface == null ? Typeface.DEFAULT : typeface);
|
||||
} else {
|
||||
oldStyle = typeface.getStyle();
|
||||
return ReactFontManager.getInstance()
|
||||
.getTypeface(fontFamilyName, typefaceStyle, assetManager);
|
||||
}
|
||||
|
||||
int newStyle = oldStyle;
|
||||
boolean italic = false;
|
||||
if (weight == UNSET) weight = Typeface.NORMAL;
|
||||
if (style == Typeface.ITALIC) italic = true;
|
||||
boolean UNDER_SDK_28 = Build.VERSION.SDK_INT < Build.VERSION_CODES.P;
|
||||
boolean applyNumericValues = !(weight < (Typeface.BOLD_ITALIC + 1) || family != null);
|
||||
boolean numericBold = UNDER_SDK_28 && weight > 699 && applyNumericValues;
|
||||
boolean numericNormal = UNDER_SDK_28 && weight < 700 && applyNumericValues;
|
||||
if (weight == Typeface.BOLD) {
|
||||
newStyle = (newStyle == Typeface.ITALIC) ? Typeface.BOLD_ITALIC : Typeface.BOLD;
|
||||
typeface = Typeface.create(typeface, newStyle);
|
||||
}
|
||||
if (weight == Typeface.NORMAL) {
|
||||
typeface = Typeface.create(typeface, Typeface.NORMAL);
|
||||
newStyle = Typeface.NORMAL;
|
||||
}
|
||||
if (style == Typeface.ITALIC) {
|
||||
newStyle = (newStyle == Typeface.BOLD) ? Typeface.BOLD_ITALIC : Typeface.ITALIC;
|
||||
typeface = Typeface.create(typeface, newStyle);
|
||||
}
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O_MR1 && weight > Typeface.BOLD_ITALIC) {
|
||||
typeface = Typeface.create(typeface, weight, italic);
|
||||
}
|
||||
if (family != null && UNDER_SDK_28 && weight > Typeface.BOLD_ITALIC) {
|
||||
FLog.d(
|
||||
TAG,
|
||||
"Support for numeric font weight numeric values with custom fonts under Android API 28 Pie is not yet supported in ReactNative.");
|
||||
}
|
||||
if (family != null) {
|
||||
typeface = ReactFontManager.getInstance().getTypeface(family, newStyle, weight, assetManager);
|
||||
}
|
||||
if (numericBold || numericNormal) {
|
||||
newStyle = numericBold ? Typeface.BOLD : Typeface.NORMAL;
|
||||
typeface = Typeface.create(typeface, newStyle);
|
||||
FLog.d(
|
||||
TAG,
|
||||
"Support for numeric font weight numeric values available only from Android API 28 Pie. Android device lower then API 28 will use normal or bold.");
|
||||
}
|
||||
return typeface;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
package com.facebook.react.views.text;
|
||||
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Build;
|
||||
import android.text.Layout;
|
||||
import android.text.TextUtils;
|
||||
|
@ -104,12 +103,7 @@ public class TextAttributeProps {
|
|||
protected @Nullable ReactAccessibilityDelegate.AccessibilityRole mAccessibilityRole = null;
|
||||
protected boolean mIsAccessibilityRoleSet = false;
|
||||
|
||||
/**
|
||||
* mFontStyle can be {@link Typeface#NORMAL} or {@link Typeface#ITALIC}. mFontWeight can be {@link
|
||||
* Typeface#NORMAL} or {@link Typeface#BOLD}.
|
||||
*/
|
||||
protected int mFontStyle = UNSET;
|
||||
|
||||
protected int mFontWeight = UNSET;
|
||||
/**
|
||||
* NB: If a font family is used that does not have a style in a certain Android version (ie.
|
||||
|
@ -460,39 +454,12 @@ public class TextAttributeProps {
|
|||
mFontFeatureSettings = TextUtils.join(", ", features);
|
||||
}
|
||||
|
||||
/**
|
||||
* /* This code is duplicated in ReactTextInputManager /* TODO: Factor into a common place they
|
||||
* can both use
|
||||
*/
|
||||
private void setFontWeight(@Nullable String fontWeightString) {
|
||||
int fontWeightNumeric =
|
||||
fontWeightString != null ? parseNumericFontWeight(fontWeightString) : -1;
|
||||
int fontWeight = UNSET;
|
||||
if (fontWeightNumeric >= 500 || "bold".equals(fontWeightString)) {
|
||||
fontWeight = Typeface.BOLD;
|
||||
} else if ("normal".equals(fontWeightString)
|
||||
|| (fontWeightNumeric != -1 && fontWeightNumeric < 500)) {
|
||||
fontWeight = Typeface.NORMAL;
|
||||
}
|
||||
if (fontWeight != mFontWeight) {
|
||||
mFontWeight = fontWeight;
|
||||
}
|
||||
mFontWeight = ReactTypefaceUtils.parseFontWeight(fontWeightString);
|
||||
}
|
||||
|
||||
/**
|
||||
* /* This code is duplicated in ReactTextInputManager /* TODO: Factor into a common place they
|
||||
* can both use
|
||||
*/
|
||||
private void setFontStyle(@Nullable String fontStyleString) {
|
||||
int fontStyle = UNSET;
|
||||
if ("italic".equals(fontStyleString)) {
|
||||
fontStyle = Typeface.ITALIC;
|
||||
} else if ("normal".equals(fontStyleString)) {
|
||||
fontStyle = Typeface.NORMAL;
|
||||
}
|
||||
if (fontStyle != mFontStyle) {
|
||||
mFontStyle = fontStyle;
|
||||
}
|
||||
mFontStyle = ReactTypefaceUtils.parseFontStyle(fontStyleString);
|
||||
}
|
||||
|
||||
private void setIncludeFontPadding(boolean includepad) {
|
||||
|
@ -601,21 +568,4 @@ public class TextAttributeProps {
|
|||
}
|
||||
return androidTextBreakStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return -1 if the input string is not a valid numeric fontWeight (100, 200, ..., 900), otherwise
|
||||
* return the weight.
|
||||
*
|
||||
* <p>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')
|
||||
: -1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* 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.graphics.Typeface;
|
||||
import android.os.Build;
|
||||
import com.facebook.infer.annotation.Nullsafe;
|
||||
|
||||
/** Responsible for normalizing style and numeric weight for backward compatibility. */
|
||||
@Nullsafe(Nullsafe.Mode.LOCAL)
|
||||
class TypefaceStyle {
|
||||
|
||||
public static final int BOLD = 700;
|
||||
public static final int NORMAL = 400;
|
||||
|
||||
private static final int MIN_WEIGHT = 1;
|
||||
private static final int MAX_WEIGHT = 1000;
|
||||
|
||||
private final boolean mItalic;
|
||||
private final int mWeight;
|
||||
|
||||
public TypefaceStyle(int weight, boolean italic) {
|
||||
mItalic = italic;
|
||||
mWeight = weight == ReactBaseTextShadowNode.UNSET ? NORMAL : weight;
|
||||
}
|
||||
|
||||
public TypefaceStyle(int style) {
|
||||
if (style == ReactBaseTextShadowNode.UNSET) {
|
||||
style = Typeface.NORMAL;
|
||||
}
|
||||
|
||||
mItalic = (style & Typeface.ITALIC) != 0;
|
||||
mWeight = (style & Typeface.BOLD) != 0 ? BOLD : NORMAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* If `weight` is supplied, it will be combined with the italic bit from `style`. Otherwise, any
|
||||
* existing weight bit in `style` will be used.
|
||||
*/
|
||||
public TypefaceStyle(int style, int weight) {
|
||||
if (style == ReactBaseTextShadowNode.UNSET) {
|
||||
style = Typeface.NORMAL;
|
||||
}
|
||||
|
||||
mItalic = (style & Typeface.ITALIC) != 0;
|
||||
mWeight =
|
||||
weight == ReactBaseTextShadowNode.UNSET
|
||||
? (style & Typeface.BOLD) != 0 ? BOLD : NORMAL
|
||||
: weight;
|
||||
}
|
||||
|
||||
public int getNearestStyle() {
|
||||
if (mWeight < BOLD) {
|
||||
return mItalic ? Typeface.ITALIC : Typeface.NORMAL;
|
||||
} else {
|
||||
return mItalic ? Typeface.BOLD_ITALIC : Typeface.BOLD;
|
||||
}
|
||||
}
|
||||
|
||||
public Typeface apply(Typeface typeface) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
||||
return Typeface.create(typeface, getNearestStyle());
|
||||
} else {
|
||||
return Typeface.create(typeface, mWeight, mItalic);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -107,8 +107,8 @@ public class ReactEditText extends AppCompatEditText
|
|||
private TextAttributes mTextAttributes;
|
||||
private boolean mTypefaceDirty = false;
|
||||
private @Nullable String mFontFamily = null;
|
||||
private int mFontWeight = ReactTypefaceUtils.UNSET;
|
||||
private int mFontStyle = ReactTypefaceUtils.UNSET;
|
||||
private int mFontWeight = UNSET;
|
||||
private int mFontStyle = UNSET;
|
||||
private boolean mAutoFocus = false;
|
||||
private boolean mDidAttachToWindow = false;
|
||||
|
||||
|
|
|
@ -200,6 +200,38 @@ exports.examples = ([
|
|||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Font Weight',
|
||||
render: function(): React.Node {
|
||||
return (
|
||||
<View>
|
||||
<TextInput
|
||||
defaultValue="Font Weight (default)"
|
||||
style={[styles.singleLine]}
|
||||
/>
|
||||
{[
|
||||
'normal',
|
||||
'bold',
|
||||
'900',
|
||||
'800',
|
||||
'700',
|
||||
'600',
|
||||
'500',
|
||||
'400',
|
||||
'300',
|
||||
'200',
|
||||
'100',
|
||||
].map(fontWeight => (
|
||||
<TextInput
|
||||
defaultValue={`Font Weight (${fontWeight})`}
|
||||
key={fontWeight}
|
||||
style={[styles.singleLine, {fontWeight}]}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Text input, themes and heights',
|
||||
render: function(): React.Node {
|
||||
|
|
Загрузка…
Ссылка в новой задаче