背景

使用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. rest frame work纪念版代码

    models.py from django.db import models from pygments.lexers import get_all_lexers from pygments.styl ...

  2. Mysql 使用delete drop truncate 删除数据时受外键约束影响解决方案

    先禁用数据库的外键约束: set foreign_key_checks=0; 进行删除操作 delete.drop.truncate 恢复数据库外键约束: set foreign_key_checks ...

  3. fastjson将java list转为json字符串

    1.直接用fastjson的静态方法string JSON.toJSONString(list)方法就行,JSON.toJSONString(list)将java list转为json字符串. 2.t ...

  4. Cocos2d-X中的菜单

    在Cocos2d-X实现显示菜单的方式比較特殊,首先须要使用CCMenu创建一个菜单,然后使用CCMenuItem创建一个菜单项,实际上程序中显示的菜单是使用CCMenu和CCMenuItemFont ...

  5. Cannot update identity column &#39;XXX&#39;

    Sqlserver -- 怎样改动设置主键的id能够手动更新? #1 打开表的设计界面 #2 将主键id的is Identity的属性改成No 这样就能够通过Update语句来更新表的主键id了. 怎 ...

  6. 大话USB驱动之总线驱动程序

    转载注明出处:http://blog.csdn.net/ruoyunliufeng/article/details/25040009 总线驱动是不用改的.内核都帮我们做好了.为了了解整个USB驱动的体 ...

  7. 自然语言处理中的Attention Model:是什么及为什么

    /* 版权声明:能够随意转载.转载时请标明文章原始出处和作者信息 .*/ author: 张俊林 要是关注深度学习在自然语言处理方面的研究进展,我相信你一定听说过Attention Model(后文有 ...

  8. 学习C语言,困难吗?

            要说计算机如今什么最火.无异于网络.看看各大计算机站点,满眼尽是网络project师,什么IP啊,路由啊,虚拟机啊,总之,操作性的计算机技能牢牢占领了半壁江山. 这些技巧当然重要.可是 ...

  9. C++中的继承与虚函数各种概念

    虚继承与一般继承 虚继承和一般的继承不同,一般的继承,在目前大多数的C++编译器实现的对象模型中,派生类对象会直接包含基类对象的字段.而虚继承的情况,派生类对象不会直接包含基类对象的字段,而是通过一个 ...

  10. IE浏览器 多版本之间切换

    由于Win7系统最低支持IE8的版本,不能通过安装IE7版本来达到要求. 有一个替代方案,通过IE自带Emulation来实现,实现步骤如下: 打开现有的IE浏览器 按下F12键 切换到“Emulat ...