React-Native WebView动态加载字体

背景
使用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动态加载字体的更多相关文章
- [RN] React Native 图片懒加载库 animated-lazy-image
React Native 图片懒加载库 animated-lazy-image 官方Github地址: https://github.com/danijelgrabez/lazy-image 使用效果 ...
- React Native两种加载图片的方式
1 加载网络图片 通过uri就可以加载网络图片 <Image source={{uri:'http://facebook.github.io/react/img/logo_og.png'}} s ...
- netcore实践:跨平台动态加载native组件
缘起netcore框架下实现基于zmq的应用. 在.net framework时代,我们进行zmq开发由很多的选择,比较常用的有clrzmq4和NetMQ. 其中clrzmq是基于libzmq的Int ...
- React router动态加载组件-适配器模式的应用
前言 本文讲述怎么实现动态加载组件,并借此阐述适配器模式. 一.普通路由例子 import Center from 'page/center'; import Data from 'page/data ...
- 微信小程序web-view之动态加载html页面
官方推出的web-view方便了很多开发人员. 我们在做的时候,经常会想到写一个小程序的page然后通过动态加载web-view的形式来完成其他功能页面的开发. 之前研究web-view的时候发现网上 ...
- React 性能优化之组件动态加载(react-loadable)
React 项目打包时,如果不进行异步组件的处理,那么所有页面所需要的 js 都在同一文件中(bundle.js),整个js文件很大,从而导致首屏加载时间过长. 所有,可以对组件进行异步加载处理,通常 ...
- Win8 Metro动态加载内容框架
制作背景 为了参加ImagineCup 2013 世界公民类比赛,我们设计制作了一个可动态扩展的幼教类App.这个App需要能动态加载内容,内容包括带动画可交互的电子书,动画,视频,游戏. 技术支持 ...
- JAVA类的静态加载和动态加载以及NoClassDefFoundError和ClassNotFoundException
我们都知道Java初始化一个类的时候可以用new 操作符来初始化, 也可通过Class.forName()的方式来得到一个Class类型的实例,然后通过这个Class类型的实例的newInstance ...
- Android中的动态加载机制
在目前的软硬件环境下,Native App与Web App在用户体验上有着明显的优势,但在实际项目中有些会因为业务的频繁变更而频繁的升级客户端,造成较差的用户体验,而这也恰恰是Web App的优势.本 ...
随机推荐
- 《Spring in action》之装配Bean
创建应用对象之间协作关系的行为通常称为装配,这也是依赖注入的本质. Spring装配Bean的三种主要机制: 1.在XML中进行显示配置 2.在java中进行显示配置 3.隐式的bean发现机制和自动 ...
- Java的动态代理(DynamicProxy)
代理的概述 代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问.代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理. 代理模式UML图 ...
- 5、Java并发性和多线程-相同线程
以下内容转自http://tutorials.jenkov.com/java-concurrency/same-threading.html(使用谷歌翻译): 相同线程(同一线程)是一种并发模型,其中 ...
- CentOS 7: 配置Network
Command Mode Step 1 » Network interface config files are located in /etc/sysconfig/network-scripts/ ...
- Speak a Good Word for SB
今天举行了英语词汇发音交流会,通过这个会议我有了非常大了感触. 一共同拥有三个环节.第一个环节读单词我们组读的单词it.pen.do.stop.think.park.sink.wood.在这一个环节中 ...
- centos中chfn命令
功能说明:改变finger指令显示的信息 假设你想改变哪个用户的finger信息,直接chfn username就可以.然后就能够输入一系列的信息 [root@centos Desktop]# chf ...
- MapReduce04
===================== MapReduce内部机制:本地性 ===================== 什么是数据本地性(data locality)--------------- ...
- Uva 10036 - Divisibility
Consider an arbitrary sequence of integers. One can place + or - operators between integers in the s ...
- YOCTO
Yocto ,是一个开源社区它通过提供模版.工具和方法帮助开发者创建基于linux内核的定制系统,支持ARM, PPC, MIPS, x86 (32 & 64 bit)硬件体系架构.
- go语言笔记——append底层实现和Cpp vector无异,只是有返回值,double后返回了新的vector地址而已
切片的复制与追加 如果想增加切片的容量,我们必须创建一个新的更大的切片并把原分片的内容都拷贝过来.下面的代码描述了从拷贝切片的 copy 函数和向切片追加新元素的 append 函数. 示例 7.12 ...