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的优势.本 ...
随机推荐
- hihoCoder #27
A QvQ B 题目:http://hihocoder.com/problemset/problem/1470 分析:dfs序+栈+数学 可以发现,对于每组询问,树上是有很多点都只能等于0的 对于每个 ...
- url处理函数
function UrlOption(url) { this.url = url || ''; this.init(); this.change = function (url) { this.url ...
- [Angular] New in V6.1
Router Scroll Position Restoration: remember and restore scroll position as the user navigates aroun ...
- [Dart] Understand Variables and Constants in Dart
In this lesson, we will look at how to create variables and constants. These are containers that sto ...
- 编程算法 - 求1+2+...+n(函数继承) 代码(C++)
求1+2+...+n(函数继承) 代码(C++) 本文地址: http://blog.csdn.net/caroline_wendy 题目: 求1+2+...+n, 要求不能使用乘除法\for\whi ...
- 【JAVA】两点经纬度直线距离的计算
来自谷歌地图的计算公式: 通过JAVA的Math类各种方法调用.实现上述公式 private static double EARTH_RADIUS = 6378.137;// 单位千米 /** * 角 ...
- spring装配集合
前面我们已经了解了怎样使用spring装备简单的属性(使用value属性)和引用其它bean的属性(使用ref属性).可是value和ref仅在Bean的属性值是单个值的情况下才实用.当bean的属性 ...
- JavaSE入门学习10:Java修饰符
Java语言提供了非常多修饰符,主要分为下面两类: 訪问修饰符 非訪问修饰符 修饰符用来定义类.方法或者变量.通常放在语句的最前端.我们通过以下的样例来说明: <span style=" ...
- 带你装B,带你飞的大数据时代
我接触过的大数据有: 1.美国棱镜计划 2.前几天新闻报道的,苹果公司窃取用户隐私 3.百度的用户搜素习惯统计分析 4.淘宝的用户购物习惯分析,智能推荐宝贝 5.浏览器的智能标签页 ... 最想了解的 ...
- 微信JS-SDK怎样使用
前两天要用到微信JS库的的一句话--wx.closeWindow();可是整个调用过程有点儿泪奔了.. .. 尽管开发人员平台上说的清清楚楚,可是使用起来就是not ok! 一,绑定域名 登录到微信公 ...