替换android系统字体颜色,Android替换系统字体

1.引言

最近老大安排一个任务,让我看看android 字体这块,将我们产品中的字体替换下。花了1.2天看懂,还得写篇文章,教程在组内进行分享。这次算是我进军Android系统的第一步。这篇文章基于Android 8.0

2.正题

2.1 font.xml字段描述

系统字体相关的文件、文件夹:fonts文件 和font.xml配置文件

系统字体存放在: System/fonts/文件夹下

系统字体配置文件 font.xml 存放在:System/etc目录下。font.xml如下所示:

Roboto-Thin.ttf

Roboto-ThinItalic.ttf

Roboto-Light.ttf

Roboto-LightItalic.ttf

Roboto-Regular.ttf

Roboto-Italic.ttf

Roboto-Medium.ttf

Roboto-MediumItalic.ttf

Roboto-Black.ttf

Roboto-BlackItalic.ttf

Roboto-Bold.ttf

Roboto-BoldItalic.ttf

NotoNaskhArabic-Regular.ttf

NotoNaskhArabic-Bold.ttf

NotoNaskhArabicUI-Regular.ttf

NotoNaskhArabicUI-Bold.ttf

NotoSansEthiopic-Regular.ttf

NotoSansEthiopic-Bold.ttf

NotoSansSC-Regular.otf

NotoSansTC-Regular.otf

family

name:源码中通过name去找到font族

lang: 国家语言

font

weight:字体的粗细

style: 字体类型 普通、加粗,斜体

index:从0开始,表示备用字体的序号,0优先加载

字体匹配规则:

当是中文的时候会寻找中文也就是zh的配置,其中zh-hans是简体,zh-hant是繁体。原生系统指定的备用字体是NotoSerifCJK-Regular.ttc。ttc是多种字体的组合字体,上述的字体是中日韩三国的综合字体。

Roboto是不支持加载中文字体的。我把下图标注的字体换成Roboto-Italic.ttf之后,发现字体还真的是能显示出斜体来。这是不是说明“ Roboto”支持中文斜体呢?

2c7ff2fa949f

image.png

其实不是,我们把文件fonts中的字体都删除只保留Roboto的字体。发现中文字体集体乱码显示不出来。由此我们可以推断。当加载中文的时候,Roboto会主动去用NotoSerifCJK-Regular.ttc来加载中文。验证方法将NotoSerifCJK-Regular.ttc 添加到fonts文件中之后。中文就显示出来了。

删除font.xml的 zh-hans/zh-hant配置之后,发现依旧能正常显示中文。说明加载中文时,系统会自动加载NotoSerifCJK-Regular.ttc字体。

知道了这些规则之后。如何更改系统中文字体呢?

我们只需要替换如下的配置:

自己的ttf

//字体加粗的中文字体,不加这句,会以NotoSerifCJK来显示加粗字体

自己的ttf

NotoSansTC-Regular.otf

自己的ttf

到此为止,替换系统字体就会生效。

上文说到:weight==400是android系统中正常字体的粗细,weight==700表示是加粗的字体。系统是在哪里加载的字体呢??是不是我们更改下代码,就能达到weight==500的时候显示正常字体,weight==100的时候字体加粗呢?

TypeFace.java

static {

final HashMap systemFontMap = new HashMap<>();

initSystemDefaultTypefaces(systemFontMap, SystemFonts.getRawSystemFallbackMap(),

SystemFonts.getAliases());

sSystemFontMap = Collections.unmodifiableMap(systemFontMap);

// We can't assume DEFAULT_FAMILY available on Roboletric.

if (sSystemFontMap.containsKey(DEFAULT_FAMILY)) {

setDefault(sSystemFontMap.get(DEFAULT_FAMILY));//创建默认“sans-serif”的TypeFace

}

// Set up defaults and typefaces exposed in public API

DEFAULT = create((String) null, 0);//正常粗细的“sans-serif”字体

DEFAULT_BOLD = create((String) null, Typeface.BOLD);//粗体 “sans-serif”类型的字体

SANS_SERIF = create("sans-serif", 0);

SERIF = create("serif", 0);

MONOSPACE = create("monospace", 0);

sDefaults = new Typeface[] {

DEFAULT,

DEFAULT_BOLD,

create((String) null, Typeface.ITALIC),

create((String) null, Typeface.BOLD_ITALIC),

};

// A list of generic families to be registered in native.

// https://www.w3.org/TR/css-fonts-4/#generic-font-families

String[] genericFamilies = {

"serif", "sans-serif", "cursive", "fantasy", "monospace", "system-ui"

};

for (String genericFamily : genericFamilies) {

registerGenericFamilyNative(genericFamily, systemFontMap.get(genericFamily));

}

}

点进Create方法,nativeCreateFromTypeface native 方法来初始化系统字体并且设置默认的系统字体以及字体样式。

public static Typeface create(Typeface family, @Style int style) {

if ((style & ~STYLE_MASK) != 0) {

style = NORMAL;

}

if (family == null) {

family = sDefaultTypeface;

}

// Return early if we're asked for the same face/style

if (family.mStyle == style) {

return family;

}

final long ni = family.native_instance;

Typeface typeface;

synchronized (sStyledCacheLock) {

SparseArray styles = sStyledTypefaceCache.get(ni);

if (styles == null) {

styles = new SparseArray(4);

sStyledTypefaceCache.put(ni, styles);

} else {

typeface = styles.get(style);

if (typeface != null) {

return typeface;

}

}

typeface = new Typeface(nativeCreateFromTypeface(ni, style));

styles.put(style, typeface);

}

return typeface;

}

/framwork/base/core/jni/android/graphics/Typeface.cpp#nativeCreateFromTypeface

static jlong Typeface_createFromTypeface(JNIEnv* env, jobject, jlong familyHandle, jint style) {

Typeface* family = toTypeface(familyHandle);

Typeface* face = Typeface::createRelative(family, (Typeface::Style)style);

// TODO: the following logic shouldn't be necessary, the above should always succeed.

// Try to find the closest matching font, using the standard heuristic

if (NULL == face) {

face = Typeface::createRelative(family, (Typeface::Style)(style ^ Typeface::kItalic));

}

for (int i = 0; NULL == face && i < 4; i++) {

face = Typeface::createRelative(family, (Typeface::Style)i);

}

return toJLong(face);

}

进一步调用真正的创建类:

/framwork/base/libs/hwui/hwui/Typeface.cpp# createRelative

Typeface* Typeface::createFromFamilies(std::vector<:shared_ptr>>&& families,

int weight, int italic) {

Typeface* result = new Typeface;

result->fFontCollection.reset(new minikin::FontCollection(families));

if (weight == RESOLVE_BY_FONT_TABLE || italic == RESOLVE_BY_FONT_TABLE) {

int weightFromFont;

bool italicFromFont;

const minikin::FontStyle defaultStyle;

const minikin::MinikinFont* mf =

families.empty()

? nullptr

: families[0]->getClosestMatch(defaultStyle).font->typeface().get();

if (mf != nullptr) {

SkTypeface* skTypeface = reinterpret_cast(mf)->GetSkTypeface();

const SkFontStyle& style = skTypeface->fontStyle();

weightFromFont = style.weight();

italicFromFont = style.slant() != SkFontStyle::kUpright_Slant;

} else {

// We can't obtain any information from fonts. Just use default values.

weightFromFont = SkFontStyle::kNormal_Weight;//默认值400

italicFromFont = false;

}

if (weight == RESOLVE_BY_FONT_TABLE) {

weight = weightFromFont;

}

if (italic == RESOLVE_BY_FONT_TABLE) {

italic = italicFromFont ? 1 : 0;

}

}

由上面的流程可以看到,系统默认的确还是400,加粗是在base-weight上加300.

上面我们知道了,TypeFace是系统加载默认字体的地方,通过jni调用告诉底层字体样式。也知道了代码中正常字体粗细是400.加粗是700。又有一个问题:TypeFace是在哪里加载的呢?

android系统启动是通过解析init.rc文件。进一步得到Zygote进程,ZygoteInit进程会产生System_Server进程。且Zygote进程会通过反射进入ZygoteInit.java main()方法。这是底层进入到java层的第一个方法。

main方法中调用了preload()方法:

2c7ff2fa949f

image.png

preload又会调用preloadClass() 预加载基础类。

/**

* The path of a file that contains classes to preload.

*/

private static final String PRELOADED_CLASSES = "/system/etc/preloaded-classes";

preloaded-classes 是一个xml,里面写入了成千上万个全路径类名。存在于System/etc 下

好了,今天的问题都弄明白了。下一期分享开机动画的知识。

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值