如何为React Native Android项目添加不同字重的字体?

38

我已经成功添加了自定义字体:

  • 将*.ttf文件放置在ProjectName/android/app/src/main/assets/fonts/中,就像这样:

    • Open Sans.ttf
    • Open Sans_italic.ttf
    • Open Sans_bold.ttf
    • Open Sans_bold_italic.ttf
  • 然后通过fontFamily: "Open Sans"设置字体家族。

但是我想使用额外的字重,例如“Semi Bold”、“Extra Bold”。我尝试将它们添加为“Open Sans_900.ttf”,并设置fontWeight: 900,但这没有起作用,它显示了字体的粗体版本。

有没有办法添加这些额外的字重呢?


您介意标记此答案吗?https://dev59.com/eVkT5IYBdhLWcg3wStkf#70247374 据我所知,这是唯一一个完全符合您要求的答案。 - Jules Sam. Randolph
6个回答

71
自从React Native 0.66.0(已测试过RN 0.66.3)以来,这是完全可行的!解决方案是使用Android的XML字体功能。我已经在这里写了一个完整的指南,以实现一致的跨平台字体体验。本文重点关注Android端。结果为: 注意:自从2019年5月7日提交的React Native版本fd6386a07eb75a8ec16b1384a3e5827dea520b64以来,已经添加了ReactFontManager::addCustomFont方法。
我将以Raleway为例,但是这个方法适用于任何字体系列!我假设您已经将整个Raleway字体系列的TTF文件提取到临时文件夹/tmp/raleway中。也就是说:
  • Raleway-Thin.ttf (100)
  • Raleway-ThinItalic.ttf
  • Raleway-ExtraLight.ttf (200)
  • Raleway-ExtraLightItalic.ttf
  • Raleway-Light.ttf (300)
  • Raleway-LightItalic.ttf
  • Raleway-Regular.ttf (400)
  • Raleway-Italic.ttf
  • Raleway-Medium.ttf (500)
  • Raleway-MediumItalic.ttf
  • Raleway-SemiBold.ttf (600)
  • Raleway-SemiBoldItalic.ttf
  • Raleway-Bold.ttf (700)
  • Raleway-BoldItalic.ttf
  • Raleway-ExtraBold.ttf (800)
  • Raleway-ExtraBoldItalic.ttf
  • Raleway-Black.ttf (900)
  • Raleway-BlackItalic.ttf
0. 找到准确的字体族名称
您需要在系统中安装otfinfo来执行此步骤。 它已经随许多Linux发行版一起提供。在MacOS上,通过lcdf-typetools brew软件包进行安装。
otfinfo --family Raleway-Regular.ttf

应该打印“Raleway”。这个值必须保留以供以后使用。 这个名称将在React的fontFamily样式中使用。

1. 复制并重命名资源到字体文件夹

mkdir android/app/src/main/res/font
cp /tmp/raleway/*.ttf android/app/src/main/res/font

我们必须按照以下规则重新命名字体文件,以符合Android资源名称的限制:
  • _替换-
  • 将任何大写字母替换为其小写对应字母。

您可以使用下面的bash脚本(确保将字体文件夹作为第一个参数):

#!/bin/bash
# fixfonts.sh

typeset folder="$1"
if [[ -d "$folder" && ! -z "$folder" ]]; then
  pushd "$folder";
  for file in *.ttf; do
    typeset normalized="${file//-/_}";
    normalized="${normalized,,}";
    mv "$file" "$normalized"
  done
  popd
fi

./fixfonts.sh /path/to/root/FontDemo/android/app/src/main/res/font

2. 创建定义文件

创建 android/app/src/main/res/font/raleway.xml 文件,并填入以下内容。基本上,我们需要为每个希望支持的 fontStyle / fontWeight 组合创建一个条目,并注册相应的资源名称。

<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:app="http://schemas.android.com/apk/res-auto">
    <font app:fontStyle="normal" app:fontWeight="100" app:font="@font/raleway_thin" />
    <font app:fontStyle="italic" app:fontWeight="100" app:font="@font/raleway_thinitalic"/>
    <font app:fontStyle="normal" app:fontWeight="200" app:font="@font/raleway_extralight" />
    <font app:fontStyle="italic" app:fontWeight="200" app:font="@font/raleway_extralightitalic"/>
    <font app:fontStyle="normal" app:fontWeight="300" app:font="@font/raleway_light" />
    <font app:fontStyle="italic" app:fontWeight="300" app:font="@font/raleway_lightitalic"/>
    <font app:fontStyle="normal" app:fontWeight="400" app:font="@font/raleway_regular" />
    <font app:fontStyle="italic" app:fontWeight="400" app:font="@font/raleway_italic"/>
    <font app:fontStyle="normal" app:fontWeight="500" app:font="@font/raleway_medium" />
    <font app:fontStyle="italic" app:fontWeight="500" app:font="@font/raleway_mediumitalic"/>
    <font app:fontStyle="normal" app:fontWeight="600" app:font="@font/raleway_semibold" />
    <font app:fontStyle="italic" app:fontWeight="600" app:font="@font/raleway_semibolditalic"/>
    <font app:fontStyle="normal" app:fontWeight="700" app:font="@font/raleway_bold" />
    <font app:fontStyle="italic" app:fontWeight="700" app:font="@font/raleway_bolditalic"/>
    <font app:fontStyle="normal" app:fontWeight="800" app:font="@font/raleway_extrabold" />
    <font app:fontStyle="italic" app:fontWeight="800" app:font="@font/raleway_extrabolditalic"/>
    <font app:fontStyle="normal" app:fontWeight="900" app:font="@font/raleway_black" />
    <font app:fontStyle="italic" app:fontWeight="900" app:font="@font/raleway_blackitalic"/>
</font-family>

3. 注册新字体

android/app/src/main/java/com/fontdemo/MainApplication.java 中,在 onCreate 方法中将字体系列名称与我们刚刚创建的资源绑定。

⚠️ 如果您要注册不同的字体,请确保将 "Raleway" 替换为在前一步中找到的名称(找到字体系列名称)。


// Add this!
import com.facebook.react.views.text.ReactFontManager;

public class MainApplication extends Application implements ReactApplication {

  // ...
  @Override
  public void onCreate() {
     super.onCreate();
     // And that line! Note that "raleway" in R.font.raleway must be the name of
     // the XML file.
     ReactFontManager.getInstance().addCustomFont(this, "Raleway", R.font.raleway);
   }
  // ...
}

4. 尽情享受吧!

现在您可以渲染了

<Text style={{ fontFamily: "Raleway", fontStyle: "italic", fontWeight: "900" }}>
  Hello world!
</Text>

在Android和iOS上都可以(只要您使用CLI链接资产,请参阅iOS方面的文档)。

我已经按照所有步骤进行了操作,但在MainApplication.java上出现了错误。错误:找不到符号 ReactFontManager.getInstance().addCustomFont(this, "Raleway", R.font.raleway); ^ 符号: 变量字体 位置: 类 R - Dhiroo Verma
你是否导入了 ReactFontManager - Jules Sam. Randolph
是的,我已经导入了: import com.facebook.react.views.text.ReactFontManager; “”它在 R 上显示错误”。 - Dhiroo Verma
你可以尝试使用gradlew clean命令(https://dev59.com/lWQn5IYBdhLWcg3wJ0a5);如果这不起作用,那么请查看引用的存储库,并尝试寻找差异 https://github.com/jsamr/react-native-font-demo - Jules Sam. Randolph
太棒了!这对我完美地起作用了。 - v_nomad
显示剩余5条评论

61
Android中对自定义字体的支持在React Native中有些受限。它不支持除“normal”和“bold”以外的字重(同时也支持“italic”“fontStyle”)。如果您需要其他自定义字重,必须将它们作为具有不同名称的单独字体添加(如David在他的答案中提到的)。
RN能够找到的唯一字体文件格式是以下格式之一:
- {fontFamilyName} - {fontFamilyName}_bold - {fontFamilyName}_italic - {fontFamilyName}_bold_italic
支持的扩展名:.ttf和.otf
这真的没有记录在任何地方(据我所知),但您可以在此处阅读字体管理器代码:https://github.com/facebook/react-native/blob/master/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactFontManager.java

我的文件名是 Tajawal-Bold.ttf,而在 iOS 上只有重命名为下划线时才会识别到一个权重:Tajawal_Bold.ttf。这样就行了!!谢谢。 - Kash
请参见https://dev59.com/eVkT5IYBdhLWcg3wStkf#70247374以获取更好的解决方案。 - Jules Sam. Randolph
我在添加underscore时也遇到了与expo-fonts相同的问题,这个方法对我也有效。 - undefined

13

即使您尝试过,Android对于使用fontWeight属性存在限制。

{ fontFamily: 'fontFamily-bold', fontWeight: 'bold' }

字体不会正确显示,您需要删除 fontWeight 以使其正常工作。

我对此的解决方案是依赖于 platform.OS 属性:

import { Platform, ... } from 'react-native';

const styles = StyleSheet.create({
   text: {
     fontFamily: 'fontFamily',
     color: '#336666',
     backgroundColor: 'transparent',
   },
   bold: Platform.OS === 'ios' ? {
     fontFamily: 'fontFamily',
     fontWeight: 'bold'
   } : {
    fontFamily: 'fontFamily-bold'
   },
});

在渲染部分:

<Text style={[styles.text, styles.bold]}>My bold text</Text>

它将在iOS和Android上运行


3
我也遇到了同样的问题,不得不从字重文件中制作“新字体”(因为它按字体名称而不是文件名工作)。使用类似FontForge这样的工具-加载字重文件(例如Open Sans_bold.ttf),并将其重命名为“Open Sans Bold”(实际名称而不是文件名),然后在react-native中使用该名称作为fontFamily(显然,将该字体附加到您的项目中)。因此,您将拥有两个字体文件:“Open Sans”和“Open Sans Bold”。希望这可以帮助你!

1
这是我的建议:
在您的assets/fonts/中,放置以下文件:
  • YourFont-Regular.tff
  • YourFont-Bold.tff

react-native 链接

package.json

“rnpm”: {
   “assets”: [“./assets/fonts/”]
}

在你的样式中,你这样写:

const styles = StyleSheet.create({
  text: {
    fontFamily: 'YourFont-Regular',
    color: '#336666',
    backgroundColor: 'transparent',
    },
  bold: {
    fontFamily: 'YourFont-Bold',
    },
  })

然后,在您的渲染中像这样:

<Text style={[styles.text, bold]}>Hello World</Text>

这种方法适用于Android和iOS。


0

我创建了一个自定义组件,将字体重量转换为带有词缀的字体族。也许列出所有字体看起来有点愚蠢,而不是使用Object.entries()...map(),但我相信保持静态样式条目可以允许StyleSheet通过 ID 正确引用在代码中频繁使用的样式。

const Typography = {
  primary: 'Inter-Regular',
  thin: 'Inter-Thin',
  extraLight: 'Inter-ExtraLight',
  light: 'Inter-Light',
  medium: 'Inter-Medium',
  semiBold: 'Inter-SemiBold',
  bold: 'Inter-Bold',
  extraBold: 'Inter-ExtraBold',
  black: 'Inter-Black',
};
export default Typography;

export function StyledText(props: TextProps & { children?: ReactNode }) {
  const { style, ...others } = props;
  if (!style) {
    return <Text {...props} />;
  }

  const { fontWeight } = StyleSheet.flatten(style);
  if (!fontWeight) {
    return <Text {...props} />;
  }

  let weightedFontFamily: StyleProp<TextStyle> | undefined;

  switch (fontWeight) {
    case '100':
      weightedFontFamily = styles.thin;
      break;
    case '200':
      weightedFontFamily = styles.extraLight;
      break;
    case '300':
      weightedFontFamily = styles.light;
      break;
    case '500':
      weightedFontFamily = styles.medium;
      break;
    case '600':
      weightedFontFamily = styles.semiBold;
      break;
    case 'bold':
    case '700':
      weightedFontFamily = styles.bold;
      break;
    case '800':
      weightedFontFamily = styles.extraBold;
      break;
    case '900':
      weightedFontFamily = styles.black;
      break;
    default:
      weightedFontFamily = styles.regular;
  }

  return <Text style={[style, weightedFontFamily]} {...others} />;
}

const styles = StyleSheet.create({
  regular: { fontFamily: Typography.primary },
  thin: { fontFamily: Typography.thin },
  extraLight: { fontFamily: Typography.extraLight },
  light: { fontFamily: Typography.light },
  medium: { fontFamily: Typography.medium },
  semiBold: { fontFamily: Typography.semiBold },
  bold: { fontFamily: Typography.bold },
  extraBold: { fontFamily: Typography.extraBold },
  black: { fontFamily: Typography.black },
});

因为我在整个应用程序中只使用了一个字体系列,所以我没有考虑fontFamily。如果您有多种字体,您将不得不从样式中获取fontFamily,但也要从主题中获取fontFamily,因为它可能会被继承。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接