背景

使用react-native构建的iOS/Android双端APP,通过WebView加载本地页面,需要根据服务器提供的字体列表实现下载和动态加载。

本地字体检查

有些字体手机操作系统已经提供了,可以不需要下载和加载。

iOS

UIFont.familyNames提供了所有系统自带字体的familyNames,直接将结果返回给RN处理即可。

SHMFontsModul.h

//
// SHMFontsModule.h
// shimo
//
// Created by Rex Rao on 2018/1/9.
// Copyright © 2018年 shimo.im. All rights reserved.
// #import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h> @interface SHMFontsModule : NSObject <RCTBridgeModule> @end

SHMFontsModule.m

//
// SHMFontsModule.m
// shimo
//
// Created by Rex Rao on 2018/1/9.
// Copyright © 2018年 shimo.im. All rights reserved.
// #import "SHMFontsModule.h"
#import <UIKit/UIKit.h> @implementation SHMFontsModule RCT_EXPORT_MODULE(SHMFonts); RCT_REMAP_METHOD(fontFamilyNames,
resolver
: (RCTPromiseResolveBlock)resolve
rejecter
: (RCTPromiseRejectBlock)reject) {
resolve(UIFont.familyNames);
} @end

Android

安卓系统没有直接提供接口返回系统字体列表,经过调研和阅读源代码,发现有一个类中的私有静态变量存储了字体信息,反射即可得到。但因为Android版本原因,低版本系统代码不同无法通过此方法得到。继续对这个静态变量顺藤摸瓜,发现Android通过解析字体xml文件来设置此变量的值,根据系统不同,字体配置xml文件的位置和结构也有所不同。

  • Android 5.1及以下

    • 路径:/system/etc/system_fonts.xml
    • 结构样例请直接查看源文件
  • Android 5.1以上
    • 路径:/system/etc/fonts.xml
    • 结构样例请直接查看源文件

Android源码中有个FontListParser类用来解析此字体配置文件,我们可以参考此类完成自己的parser,分两种配置路径和结构获取系统的Font Families,然后传给RN处理。

FontListParser.java

package chuxin.shimo.shimowendang.fonts;

import android.util.Xml;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException; import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List; /**
* Created by sohobloo on 2018/1/17.
*/ /**
* Parser for font config files.
*
*/
public class FontListParser { public static List<String> parse(InputStream in) throws XmlPullParserException, IOException {
List<String> familyNames = new ArrayList<String>();
try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(in, null);
parser.nextTag();
parser.require(XmlPullParser.START_TAG, null, "familyset");
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) {
continue;
}
String tag = parser.getName();
switch (tag) {
case "family": {
String name = parser.getAttributeValue(null, "name");
if (name != null && !name.isEmpty()) {
familyNames.add(name);
skip(parser);
} else {
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) {
continue;
}
tag = parser.getName();
if (tag.equals("nameset")) {
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) {
continue;
}
tag = parser.getName();
if (tag.equals("name")) {
name = parser.nextText();
if (name != null && !name.isEmpty()) {
familyNames.add(name);
}
} else {
skip(parser);
}
}
} else {
skip(parser);
}
}
}
break;
}
case "alias": {
String name = parser.getAttributeValue(null, "name");
if (name != null && !name.isEmpty()) {
familyNames.add(name);
}
skip(parser);
break;
}
default:
skip(parser);
break;
}
}
} finally {
in.close();
} return familyNames;
} private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
int depth = 1;
while (depth > 0) {
switch (parser.next()) {
case XmlPullParser.START_TAG:
depth++;
break;
case XmlPullParser.END_TAG:
depth--;
break;
default:
break;
}
}
}
}

FontsModule.java

package chuxin.shimo.shimowendang.fonts;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableArray; import org.xmlpull.v1.XmlPullParserException; import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.List; /**
* Created by sohobloo on 2018/1/9.
*/ public class FontsModule extends ReactContextBaseJavaModule {
private static final String MODULE_NAME = "SHMFonts"; private static final String SYSTEM_CONFIG_LOCATION = "/system/etc/";
private static final String FONTS_CONFIG = "fonts.xml";
private static final String SYSTEM_FONTS_CONFIG = "system_fonts.xml"; FontsModule(ReactApplicationContext reactContext) {
super(reactContext);
} @Override
public String getName() {
return MODULE_NAME;
} @ReactMethod
public void fontFamilyNames(Promise promise) {
WritableArray familyNames = null;
File systemFontConfigLocation = new File(SYSTEM_CONFIG_LOCATION);
File configFilename = new File(systemFontConfigLocation, FONTS_CONFIG);
if (!configFilename.exists()) {
configFilename = new File(systemFontConfigLocation, SYSTEM_FONTS_CONFIG);
}
if (configFilename.exists()) {
try {
FileInputStream fontsIn = new FileInputStream(configFilename);
List<String> familyNameList = FontListParser.parse(fontsIn);
familyNames = Arguments.fromList(familyNameList);
} catch (XmlPullParserException | IOException e) {
e.printStackTrace();
}
} promise.resolve(familyNames); }
}

FontsPackage.java

package chuxin.shimo.shimowendang.fonts;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; /**
* Created by sohobloo on 2018/1/9.
*/ public class FontsPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new FontsModule(reactContext));
return modules;
} @Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}

RN

RN端通过对比项目需要加载的字体和调用原生iOS/Android模块获取到的系统字体列表交叉对比即可知道哪些字体系统以及存在了。对比时注意一下FamilyName的大小写/空格以及「-」连接符。

下载字体

下载不是本topic的主题,就不细讲了。下载到App目录中即可,下载前判断一下是否已经下载云云。

由于iOS的WKWebView没有读取Documents目录权限导致真机无法加载字体资源,根据调研需要拷贝字体文件到tmp/www/fonts目录中。参考

WebView动态加载字体

字体可以通过CSS的font-face来加载,这里就简单了,通过insertRule传入本地字体的familyName和path即可动态加载

function loadFontFace (name, path) {
const sheet = document.styleSheets[0]
sheet.insertRule(`@font-face {font-family: '${name}'; src:url('${path}');}`, sheet.cssRules.length || 0)
}

通过调用此js函数注入webview即可实现动态加载字体,无需刷新更无需重启APP。:-p

博客园的MarkDown没有预览功能吗?难道大神们写文章都这么牛X了,排版了然于心?

React-Native WebView动态加载字体的更多相关文章

  1. [RN] React Native 图片懒加载库 animated-lazy-image

    React Native 图片懒加载库 animated-lazy-image 官方Github地址: https://github.com/danijelgrabez/lazy-image 使用效果 ...

  2. React Native两种加载图片的方式

    1 加载网络图片 通过uri就可以加载网络图片 <Image source={{uri:'http://facebook.github.io/react/img/logo_og.png'}} s ...

  3. netcore实践:跨平台动态加载native组件

    缘起netcore框架下实现基于zmq的应用. 在.net framework时代,我们进行zmq开发由很多的选择,比较常用的有clrzmq4和NetMQ. 其中clrzmq是基于libzmq的Int ...

  4. React router动态加载组件-适配器模式的应用

    前言 本文讲述怎么实现动态加载组件,并借此阐述适配器模式. 一.普通路由例子 import Center from 'page/center'; import Data from 'page/data ...

  5. 微信小程序web-view之动态加载html页面

    官方推出的web-view方便了很多开发人员. 我们在做的时候,经常会想到写一个小程序的page然后通过动态加载web-view的形式来完成其他功能页面的开发. 之前研究web-view的时候发现网上 ...

  6. React 性能优化之组件动态加载(react-loadable)

    React 项目打包时,如果不进行异步组件的处理,那么所有页面所需要的 js 都在同一文件中(bundle.js),整个js文件很大,从而导致首屏加载时间过长. 所有,可以对组件进行异步加载处理,通常 ...

  7. Win8 Metro动态加载内容框架

    制作背景 为了参加ImagineCup 2013 世界公民类比赛,我们设计制作了一个可动态扩展的幼教类App.这个App需要能动态加载内容,内容包括带动画可交互的电子书,动画,视频,游戏. 技术支持 ...

  8. JAVA类的静态加载和动态加载以及NoClassDefFoundError和ClassNotFoundException

    我们都知道Java初始化一个类的时候可以用new 操作符来初始化, 也可通过Class.forName()的方式来得到一个Class类型的实例,然后通过这个Class类型的实例的newInstance ...

  9. Android中的动态加载机制

    在目前的软硬件环境下,Native App与Web App在用户体验上有着明显的优势,但在实际项目中有些会因为业务的频繁变更而频繁的升级客户端,造成较差的用户体验,而这也恰恰是Web App的优势.本 ...

随机推荐

  1. 这段百度问答,对我相关有对啊!!!----如何获取Windows系统登陆用户名

    如何获取Windows系统登陆用户名 http://zhidao.baidu.com/link?url=Hva9PkVwYZv8KSEWftSqTWe8fqM1dhoq59BurnfADmcOvFjF ...

  2. 51Nod——T 1631 小鲨鱼在51nod小学

    https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1631 基准时间限制:1 秒 空间限制:131072 KB 分值: 20 ...

  3. docker run和nsenter

    docker run和nsenter 1,本机运行java -version没有内容, 使用 docker run java java -version就有内容了: 2, docker run -d ...

  4. HDU 1241 Oil Deposits (DFS)

    题目链接:Oil Deposits 解析:问有多少个"@"块.当中每一个块内的各个"@"至少通过八个方向之中的一个相邻. 直接从"@"的地方 ...

  5. 深入学习理解java-ThreadLocal

    导读 首先,ThreadLocal 不是用来解决共享对象的多线程訪问问题的,普通情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其它线程是不须要訪问的,也訪问不到 ...

  6. Windows 9立即公布了

    Windows 9技术预览版可能于今晚在美国旧金山举办的Windows公布会上宣布.下一代的Windows名称最有可能是Windows或Windows 9.网友猜想也可能是WIndows X或Wind ...

  7. oracle 创建暂时表

    提交事务后会自己主动清空,不同的事务之间数据隔离 create global temporary table GLS_REPORT_EXPR (   EXPRNO VARCHAR2(30) not n ...

  8. 第四章、TIny4412 U-BOOT移植四 配置时钟频率源码分析【转】

    本文转载自:http://blog.csdn.net/eshing/article/details/37542459 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[+]   上 ...

  9. Silverlight 2学习笔记一:初识Silverlight

    Silverlight,问世至今已有好一段时日了,向来只是只闻其名,不知其实,今天终于对Silverlight有了点初步的了解. 一.Silverlight是什么?Sliverlight是基于.NET ...

  10. bzoj2300

    http://www.lydsy.com/JudgeOnline/problem.php?id=2300 终于对了... 平衡树又写挂了...不要忘记清空原先的root和修改root... #incl ...