源码阅读之TextView(1)--XMLTypefaceAtt
导读:本文TextView基于android-28版本,代码行数12551.
► 包
- TextView位于包 android.widget;
该包下包含很多常用的UI控件,例如Switch、ScrollView、ListView等等。
► 父类
- 继承自View
View的源码阅读链接:敬请期待 - 实现了ViewTreeObserver.OnPreDrawListener
在将要绘制视图树时要调用的回调方法。 此时,树中的所有视图均已测量并指定了框架。 可以使用它来调整滚动范围,甚至可以在绘制之前请求新的布局。
/**
* Interface definition for a callback to be invoked when the view tree is about to be drawn.
*/
public interface OnPreDrawListener {
/**
* Callback method to be invoked when the view tree is about to be drawn. At this point, all
* views in the tree have been measured and given a frame. Clients can use this to adjust
* their scroll bounds or even to request a new layout before drawing occurs.
*
* @return Return true to proceed with the current drawing pass, or false to cancel.
*
* @see android.view.View#onMeasure
* @see android.view.View#onLayout
* @see android.view.View#onDraw
*/
public boolean onPreDraw();
}
► 结构
image.png· XMLTypefaceAttr
这个结构块包含了@IntDef注解,@Retention注解以及@interface注解
XMLTypefaceAttr是TextView中的一个由@interface 定义的自定义注解,其中初始化了四种类别,分别为DEFAULT_TYPEFACE、SANS、SERIF、MONOSPACE。
在xml中通过“typeface”属性调用。
// Enum for the "typeface" XML parameter.
// TODO: How can we get this from the XML instead of hardcoding it here?
/** @hide */
@IntDef(value = {DEFAULT_TYPEFACE, SANS, SERIF, MONOSPACE})
@Retention(RetentionPolicy.SOURCE)
public @interface XMLTypefaceAttr{}
private static final int DEFAULT_TYPEFACE = -1;
private static final int SANS = 1;
private static final int SERIF = 2;
private static final int MONOSPACE = 3;
❶ @IntDef
@IntDef(value = {DEFAULT_TYPEFACE, SANS, SERIF, MONOSPACE})
表示整数类型的带注释元素, 代表逻辑类型,并且其值应为显式命名的常量之一。 如果IntDef#flag()属性设置为true,则可以组合多个常量。也就是flag这个值决定了是否常量可以被当成一个标识,或仅可作为枚举值使用。
例如下两种写法:
/**此时定义的常量值仅作为枚举值使用**/
@IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
/**此时定义的常量值可作为标识符使用**/
@IntDef(
flag = true,
value = {NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
❷ @Rentention
@Retention(RetentionPolicy.SOURCE)
表示带有注释类型的注释将保留多长时间。 如果注释类型声明上没有保留注释,则保留策略默认为RetentionPolicy.CLASS。
此处RetentionPolicy.SOURCE表示当前定义将在编译之后被丢弃。
此外还有其他两种策略分别为:
RetentionPolicy.CLASS :注释将由编译器记录在类文件中,但VM不必在运行时保留它们。
RetentionPolicy. RUNTIME:注释将由编译器记录在类文件中,并在运行时由VM保留,因此可以通过反射方式读取它们。
另外只有当元注释类型直接用于注释时,保留元注释才有效。 如果将元注释类型用作其他注释类型的成员类型,则无效。
SOURCE:在源文件中有效,编译过程中会被忽略
CLASS:随源文件一起编译在class文件中,运行时忽略
RUNTIME:在运行时有效
参考:https://juejin.cn/post/6844903949233815566
❸ XMLTypefaceAttr的使用
- 单独看此块代码,只有当typeface和familyName都为null时,才会进入typefaceIndex的判断逻辑。
/**
* Sets the Typeface taking into account the given attributes.
*设置字体时要考虑的字体属性
* @param typeface a typeface
* @param familyName family name string, e.g. "serif"
* @param typefaceIndex an index of the typeface enum, e.g. SANS, SERIF.
* @param style a typeface style
* @param weight a weight value for the Typeface or -1 if not specified.
*/
private void setTypefaceFromAttrs(@Nullable Typeface typeface, @Nullable String familyName,
@XMLTypefaceAttr int typefaceIndex, @Typeface.Style int style,
@IntRange(from = -1, to = Typeface.MAX_WEIGHT) int weight) {
if (typeface == null && familyName != null) {
// Lookup normal Typeface from system font map.
final Typeface normalTypeface = Typeface.create(familyName, Typeface.NORMAL);
resolveStyleAndSetTypeface(normalTypeface, style, weight);
} else if (typeface != null) {
resolveStyleAndSetTypeface(typeface, style, weight);
} else { // both typeface and familyName is null.
switch (typefaceIndex) {
case SANS:
resolveStyleAndSetTypeface(Typeface.SANS_SERIF, style, weight);
break;
case SERIF:
resolveStyleAndSetTypeface(Typeface.SERIF, style, weight);
break;
case MONOSPACE:
resolveStyleAndSetTypeface(Typeface.MONOSPACE, style, weight);
break;
case DEFAULT_TYPEFACE:
default:
resolveStyleAndSetTypeface(null, style, weight);
break;
}
}
}
- 结合TextView整个代码来看,setTypefaceFromAttrs涉及的逻辑大概如下:
image.png
当TextView所涉及到的关于属性的数值传递到setTypefaceFromAttrs方法时,(typeface即对应TextView中的同名属性,familyName即对应TextView属性fontFamily),对以下三种情况:
❶ typeface为null,fontName不为null- 查看Typeface的源码可以发现系统事先初始化了由fontName和Typeface组成的静态map集合,
当typeface为null,但fontName不为null,则默认向这个map集合中获取属性为Normal的Typeface。
- 查看Typeface的源码可以发现系统事先初始化了由fontName和Typeface组成的静态map集合,
static final Map<String, Typeface> sSystemFontMap;
......
if (typeface == null && familyName != null) {
// Lookup normal Typeface from system font map.
final Typeface normalTypeface = Typeface.create(familyName, Typeface.NORMAL);
resolveStyleAndSetTypeface(normalTypeface, style, weight);
}
- resolveStyleAndSetTypeface方法
private void resolveStyleAndSetTypeface(@NonNull Typeface typeface, @Typeface.Style int style,
@IntRange(from = -1, to = Typeface.MAX_WEIGHT) int weight) {
//@IntRange限制输入的weight元素范围从-1至MAX_WEIGHT(1000),
//weight对应TextView布局中的textFontWeight属性(1-1000)表示字的厚度。
if (weight >= 0) {
weight = Math.min(Typeface.MAX_WEIGHT, weight);
final boolean italic = (style & Typeface.ITALIC) != 0;
//创建最符合指定的现有字体以及指定的粗细和斜体样式的字体对象
setTypeface(Typeface.create(typeface, weight, italic));
} else {
//创建最符合指定的现有字体以及指定的粗细和斜体样式的字体对象
setTypeface(typeface, style);
}
}
❷ typeface不为null,直接调用resolveStyleAndSetTypeface方法创建字体相关属性
if (typeface != null) {
resolveStyleAndSetTypeface(typeface, style, weight);
}
❸typeface和fontName都为null
typefaceIndex在TextAppearanceAttributes静态内部类的构造方法中设置默认值-1,对应xml中typeface在枚举变量中的位置
switch (typefaceIndex) {
case SANS:
resolveStyleAndSetTypeface(Typeface.SANS_SERIF, style, weight);
break;
case SERIF:
resolveStyleAndSetTypeface(Typeface.SERIF, style, weight);
break;
case MONOSPACE:
resolveStyleAndSetTypeface(Typeface.MONOSPACE, style, weight);
break;
case DEFAULT_TYPEFACE:
default:
resolveStyleAndSetTypeface(null, style, weight);
break;
}