背景

使用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. C语言的数组初始化

    http://blog.csdn.net/sibylle/article/details/2026915 一直以为 int a[256]={0};是把a的所有元素初始化为0,int a[256]={1 ...

  2. hive学习笔记-表操作

    Hive数据类型 基本数据类型 tinyint,smallint,int,biging,float,double,decimal,char,varchar,string,binary,boolean, ...

  3. SSD性能优化记录

    在上一篇博文中,我设计了一个优化方法,方法从业务角度出发,将切图操作涉及到的性能路径剖析出来,分别进行优化,效果显著. 眼下的情况是:一张ArcGIS武汉市城市影像图.该操作由79小时缩短至当前的67 ...

  4. WEB应用与站点的差别以及未来发展推測

    WEB应用与站点的差别 确切的说应该是网络应用(Web Application)与网络网站(Website)的差别. 之所以要弄清这两个的差别,对于网页设计师以及參与到互联网行业的职业,其方发展向有非 ...

  5. WinMain和MFC的差别

    API(Application Programming Interface):开放给应用程序调用的系统功能. 一个Windows Application(SDK): WinMain ReristerC ...

  6. 最简单的基于FFmpeg的移动端样例:Android HelloWorld

    ===================================================== 最简单的基于FFmpeg的移动端样例系列文章列表: 最简单的基于FFmpeg的移动端样例:A ...

  7. MapReduce03

    ======================== MapReduce 2.0基本架构 ======================== Client -------------> 与MapRed ...

  8. LeetCode241——Different Ways to Add Parentheses

    Given a string of numbers and operators, return all possible results from computing all the differen ...

  9. python 执行shell

    一.import os ex: 1.os.system('ls') ----并不能得到返回值 2.output = os.popen('ls') res = output.read() ----能得到 ...

  10. Linux下的IPC-UNIX Domain Socket【转】

    本文转载自:http://blog.csdn.net/guxch/article/details/7041052 一. 概述 UNIX Domain Socket是在socket架构上发展起来的用于同 ...