add support for native/downloadable fonts (#23865)

Summary:
Android API 26 and Android Support Library 26 added support for font resource type and native/downloadable fonts. It allows apps to easily download fonts from online providers, but also use of various font weights other than normal and bold, like medium. So it deprecated APIs for asset fonts, and should be removed in the future.

Advantages:
- Just copy font files in res/font and use it specifying filename (without extension) in fontFamily
- Define custom font-family using XML file (in res/font) and font files, it may have many weights and styles. See PR for example.
- Define configuration to download fonts from online font providers, and use it.

See https://developer.android.com/guide/topics/ui/look-and-feel/fonts-in-xml and https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts

[Android] [Changed] - add support for custom/downloadable fonts
Pull Request resolved: https://github.com/facebook/react-native/pull/23865

Differential Revision: D14506542

Pulled By: hramos

fbshipit-source-id: 67ba3148fb4b548cdbc779213cf6c1b2c3baffd2
This commit is contained in:
Dulmandakh
2019-04-10 13:38:05 -07:00
committed by Facebook Github Bot
parent d338a60cbe
commit f01c4e2a14
9 changed files with 60 additions and 15 deletions

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:app="http://schemas.android.com/apk/res-auto">
<font app:fontStyle="normal" app:fontWeight="400" app:font="@font/srisakdi_regular"/>
<font app:fontStyle="normal" app:fontWeight="700" app:font="@font/srisakdi_bold" />
</font-family>

Binary file not shown.

Binary file not shown.

View File

@@ -182,6 +182,14 @@ class TextExample extends React.Component<{}> {
<Text style={{fontFamily: 'notoserif', fontStyle: 'italic'}}>
NotoSerif Italic (Missing Font file)
</Text>
<Text style={{fontFamily: 'srisakdi'}}>Srisakdi Regular</Text>
<Text
style={{
fontFamily: 'srisakdi',
fontWeight: 'bold',
}}>
Srisakdi Bold
</Text>
</View>
</View>
</RNTesterBlock>

View File

@@ -7,8 +7,10 @@
package com.facebook.react.views.text;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Paint;
import android.graphics.Typeface;
@@ -29,31 +31,30 @@ public class CustomStyleSpan extends MetricAffectingSpan implements ReactSpan {
* Fonts are retrieved and cached using the {@link ReactFontManager}
*/
private final AssetManager mAssetManager;
private final int mStyle;
private final int mWeight;
private final @Nullable String mFontFamily;
private final Context mContext;
public CustomStyleSpan(
int fontStyle,
int fontWeight,
@Nullable String fontFamily,
AssetManager assetManager) {
@Nonnull Context context) {
mStyle = fontStyle;
mWeight = fontWeight;
mFontFamily = fontFamily;
mAssetManager = assetManager;
mContext = context;
}
@Override
public void updateDrawState(TextPaint ds) {
apply(ds, mStyle, mWeight, mFontFamily, mAssetManager);
apply(ds, mStyle, mWeight, mFontFamily, mContext);
}
@Override
public void updateMeasureState(TextPaint paint) {
apply(paint, mStyle, mWeight, mFontFamily, mAssetManager);
public void updateMeasureState(@Nonnull TextPaint paint) {
apply(paint, mStyle, mWeight, mFontFamily, mContext);
}
/**
@@ -82,7 +83,7 @@ public class CustomStyleSpan extends MetricAffectingSpan implements ReactSpan {
int style,
int weight,
@Nullable String family,
AssetManager assetManager) {
Context context) {
int oldStyle;
Typeface typeface = paint.getTypeface();
if (typeface == null) {
@@ -103,7 +104,7 @@ public class CustomStyleSpan extends MetricAffectingSpan implements ReactSpan {
}
if (family != null) {
typeface = ReactFontManager.getInstance().getTypeface(family, want, assetManager);
typeface = ReactFontManager.getInstance().getTypeface(family, want, context);
} else if (typeface != null) {
// TODO(t9055065): Fix custom fonts getting applied to text children with different style
typeface = Typeface.create(typeface, want);
@@ -116,5 +117,4 @@ public class CustomStyleSpan extends MetricAffectingSpan implements ReactSpan {
}
paint.setSubpixelText(true);
}
}

View File

@@ -188,7 +188,7 @@ public abstract class ReactBaseTextShadowNode extends LayoutShadowNode {
textShadowNode.mFontStyle,
textShadowNode.mFontWeight,
textShadowNode.mFontFamily,
textShadowNode.getThemedContext().getAssets())));
textShadowNode.getThemedContext())));
}
if (textShadowNode.mIsUnderlineTextDecorationSet) {
ops.add(new SetSpanOperation(start, end, new ReactUnderlineSpan()));

View File

@@ -12,10 +12,14 @@ import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Typeface;
import android.util.SparseArray;
import androidx.core.content.res.ResourcesCompat;
/**
* 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
@@ -37,9 +41,11 @@ public class ReactFontManager {
private static ReactFontManager sReactFontManagerInstance;
private Map<String, FontFamily> mFontCache;
private Map<String, Typeface> mTypeCache;
private ReactFontManager() {
mFontCache = new HashMap<>();
mTypeCache = new HashMap<>();
}
public static ReactFontManager getInstance() {
@@ -49,8 +55,7 @@ public class ReactFontManager {
return sReactFontManagerInstance;
}
public
@Nullable Typeface getTypeface(
private @Nullable Typeface getTypeface(
String fontFamilyName,
int style,
AssetManager assetManager) {
@@ -71,6 +76,33 @@ public class ReactFontManager {
return typeface;
}
public @Nullable Typeface getTypeface(
String fontFamilyName,
int style,
Context context) {
Typeface font = mTypeCache.get(fontFamilyName);
if (font != null) {
return Typeface.create(
font,
style
);
}
int fontId = context.getResources().getIdentifier(fontFamilyName, "font", context.getPackageName());
if (fontId != 0) {
font = ResourcesCompat.getFont(context, fontId);
if (font != null) {
mTypeCache.put(fontFamilyName, font);
return Typeface.create(
font,
style
);
}
}
return getTypeface(fontFamilyName, style, context.getAssets());
}
/**
* Add additional font family, or replace the exist one in the font memory cache.
* @param style

View File

@@ -97,7 +97,7 @@ public class TextLayoutManager {
textAttributes.mFontStyle,
textAttributes.mFontWeight,
textAttributes.mFontFamily,
context.getAssets())));
context)));
}
if (textAttributes.mIsUnderlineTextDecorationSet) {
ops.add(new SetSpanOperation(start, end, new ReactUnderlineSpan()));

View File

@@ -221,7 +221,7 @@ public class ReactTextInputManager extends BaseViewManager<ReactEditText, Layout
Typeface newTypeface = ReactFontManager.getInstance().getTypeface(
fontFamily,
style,
view.getContext().getAssets());
view.getContext());
view.setTypeface(newTypeface);
}