android开发分享Android字体相关知识总结

目录一、android 默认字体介绍1、android 系统默认使用的是一款叫做 roboto 的字体,这也是 google 推荐使用的一款字体 传送门。它提供了多种字体形式的选择,例如:粗体,斜体等

目录

    一、android 默认字体介绍

    1、android 系统默认使用的是一款叫做 roboto 的字体,这也是 google 推荐使用的一款字体 传送门。它提供了多种字体形式的选择,例如:粗体,斜体等等。

    2、在 android 中,我们一般会直接或间接的通过 textview 控件去承载字体的显示,因为关于 android 提供的承载字体显示的控件都会直接或间接继承 textview,例如:edittext,button 等等,下面给出一张 textview 继承图:

    Android字体相关知识总结

    3、textview 中有三个属性可以设置字体的显示:

    1)、textstyle

    2)、typeface

    3)、fontfamily

    下面我们重点介绍下这三个属性

    二、textstyle

    textstyle 主要用来设置字体的样式,我们看下它在 textview 的自定义属性中的一个体现:

      //textview 的自定义属性 textstyle  <attr name="textstyle">      <flag name="normal" value="0" />      <flag name="bold" value="1" />      <flag name="italic" value="2" />  </attr>  

    从上述自定义属性中我们可以知道:

    1、textstyle 主要有 3 种样式:

    • normal:默认字体
    • bold:粗体
    • italic:斜体

    2、textstyle 是用 flag 来承载的,flag 表示的值可以做或运算,也就是说我们可以设置多种字体样式进行叠加

    接下来我们在 xml 中设置一下,如下图:

    Android字体相关知识总结

    可以看到,我们给 textview 的 textstyle 属性设置了粗体和斜体两种样式叠加,右边可以看到预览效果

    同样我们也可以在代码中对其进行设置,但是在代码中设置字体样式只能设置一种,不能叠加:

      mtextview.settypeface(null, typeface.bold)  

    三、typeface

    typeface 主要用于设置 textview 的字体,我们看下它在 textview 的自定义属性中的一个体现:

      //textview 的自定义属性 typeface  <attr name="typeface">      <enum name="normal" value="0" />      <enum name="sans" value="1" />      <enum name="serif" value="2" />      <enum name="monospace" value="3" />  </attr>  

    从上述自定义属性中我们可以知道:

    1、typeface 提供了 4 种字体:

    • noraml:普通字体,系统默认使用的字体
    • sans:非衬线字体
    • serif:衬线字体
    • monospace:等宽字体

    2、typeface 是用 enum 来承载的,enum 表示枚举类型,每次只能选择一个,因此我们每次只能设置一种字体,不能叠加

    接下来我们在 xml 中设置一下,如下图:

    Android字体相关知识总结

    简单介绍这几种字体的区别:

    serif (衬线字体):在字的笔划开始及结束的地方有额外的装饰,而且笔划的粗细会因直横的不同而有不同相

    sans (非衬线字体):没有 serif 字体这些额外的装饰,和 noraml 字体是一样的

    Android字体相关知识总结

    monospace (等宽字体):限制每个字符的宽度,让它们达到一个等宽的效果

    同样我们也可以在代码中进行设置:

      mtv.settypeface(typeface.serif)  

    四、fontfamily 

    fontfamily 相当于是加强版的 typeface,它表示 android 系统支持的一系列字体,每个字体都有一个别名,我们通过别名就能设置这种字体,看下它在 textview 的自定义属性中的一个体现:

      //textview 的自定义属性 fontfamily  <attr name="fontfamily" format="string" />  

    从上述自定义属性中我们可以知道:

    fontfamily 接收的是一个 string 类型的值,也就是我们可以通过字体别名设置这种字体,如下图:

    Android字体相关知识总结

    可以看到,它细致的区分了每个系列字体的样式,同样我们在 xml 中对它进行一个设置:

    Android字体相关知识总结

    我们在代码中在对他进行一个设置:

      mtv.settypeface(typeface.create("sans-serif-medium",typeface.normal))  

    值的注意的是:fontfamily 设置的某些字体有兼容性问题,如我上面设置的 sans-serif-medium 字体,它在 android 系统版本大于等于 21 才会生效,如果小于 21 ,则会使用默认字体,因此我们在使用 fontfamily 属性时,需要注意这个问题

    到这里,我们就把影响 android 字体的 3 个属性给讲完了,但是我心里有个疑问🤔️ ?假设我这三个属性同时设置,会一起生效吗?

    带着这个问题,我们探索一下源码

    五、textstyle,typeface,fontfamily 三者关系分析

    textview 在我们使用它之前需进行一个初始化,最终会调用它参数最多的那个构造方法:

      public textview(context context, @nullable attributeset attrs, int defstyleattr, int defstyleres) {      super(context, attrs, defstyleattr, defstyleres);    	//省略成吨代码.....    	//读取设置的属性    	readtextappearance(context, appearance, attributes, false /* stylearray */);    	//设置字体    	applytextappearance(attributes);   }    private void applytextappearance(textappearanceattributes attributes) {     	//省略成吨代码.....    	settypefacefromattrs(attributes.mfonttypeface, attributes.mfontfamily,                  attributes.mtypefaceindex, attributes.mtextstyle, attributes.mfontweight);  }  

    上面这条调用链,首先会读取 textview 设置的相关属性,我们看下与字体相关的几个:

      private void readtextappearance(context context, typedarray appearance,              textappearanceattributes attributes, boolean stylearray) {    	//...    	switch (index) {   	    case com.android.internal.r.styleable.textappearance_typeface:                  attributes.mtypefaceindex = appearance.getint(attr, attributes.mtypefaceindex);                  if (attributes.mtypefaceindex != -1 && !attributes.mfontfamilyexplicit) {                      attributes.mfontfamily = null;                  }                  break;              case com.android.internal.r.styleable.textappearance_fontfamily:                  if (!context.isrestricted() && context.canloadunsaferesources()) {                      try {                          attributes.mfonttypeface = appearance.getfont(attr);                      } catch (unsupportedoperationexception | resources.notfoundexception e) {                          // expected if it is not a font resource.                      }                  }                  if (attributes.mfonttypeface == null) {                      attributes.mfontfamily = appearance.getstring(attr);                  }                  attributes.mfontfamilyexplicit = true;                  break;              case com.android.internal.r.styleable.textappearance_textstyle:                  attributes.mtextstyle = appearance.getint(attr, attributes.mtextstyle);                  break;              //...     	    default:       }  }  

    从上述代码中我们可以看到:

    1、当我们设置 typeface 属性时,会将对应的属性值赋给 mtypefaceindex ,并把 mfontfamily 置为 null

    2、当我们设置 fontfamily 属性时,首先会通过 appearance.getfont() 方法去获取字体文件,如果能获取到,则赋值给 mfonttypeface,如果获取不到,则通过 appearance.getstring() 方法取获取当前字体别名并赋值给 mfontfamily

    注意:当我们给 fontfamily 设置了一些第三方字体,那么此时 appearance.getfont() 方法就获取不到字体

    3、当我们设置 textstyle 属性时,会将获取的属性值赋给 mtextstyle

    上述方法走完了,会调 settypefacefromattrs() 方法,这个方法就是最终 textview 设置字体的方法,我们来解析下这个方法:

      private void settypefacefromattrs(@nullable typeface typeface, @nullable string familyname,              @xmltypefaceattr int typefaceindex, @typeface.style int style,              @intrange(from = -1, to = fontstyle.font_weight_max) 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;          }      }  }  

    上述代码步骤:

    1、当 typeface 为空并且 familyname 不为空时,取 familyname 的字体

    2、当 typeface 不为空并且 familyname 为空时,取 typeface 的字体

    3、当 typeface 和 familyname 都为空,则根据 typefaceindex 的值取相应的字体

    4、typeface ,familyname 和 typefaceindex 在我们分析的 readtextappearance 方法会被赋值

    5、resolvestyleandsettypefce 方法会进行字体和字体样式的设置

    6、style 是在 readtextappearance 方法中赋值的,他和设置字体并不冲突

    好,现在代码分析的差不多了,我们再来看下上面那个疑问?我们使用假设法来进行推导:

    假设在 xml 中, typeface,familyname 和 textstyle 我都设置了,那么根据上面分析:

    1、textstyle 肯定会生效

    2、当设置了 typeface 属性,typefaceindex 会被赋值,同时 familyname 会置为空

    3、当设置了 familyname 属性,分情况:1、如果设置的是系统字体,typeface 会被赋值,familyname 还是为空。2、如果设置的是第三方字体,typeface 为空,familyname 被赋值

    因此,当我们设置了这个三个属性,typeface 和 familyname 总有一个不会为空,因此不会走第三个条件体,那么 typeface 设置的属性就不会生效了,而剩下的两个属性都能够生效

    最后对这三个属性做一个总结:

    1、fontfamily、typeface 属性用于字体设置,如果都设置了,优先使用 fontfamily 属性,typeface 属性不会生效

    2、textstyle 用于字体样式设置,与字体设置不会产生冲突

    上面这段源码分析可能有点绕,如果有不清楚的地方,欢迎评论区给我留言提问

    六、textview 设置字体属性源码分析

    通过上面源码的分析,我们清楚了 fontfamily,typeface 和 textstyle 这三者的关系。接下来我们研究一下,我们设置的这些属性是怎么实现这些效果的呢?又到了源码分析环节😂,可能会有点枯燥,但是如果你能够认真看完,一定会收获很多,干就完了

    我们上面用 xml 或代码设置的字体属性,最终都会走到 textview 的 settypeface 重载方法:

      //重载方法一  public void settypeface(@nullable typeface tf) {      if (mtextpaint.gettypeface() != tf) {        	//通过 mtextpaint 设置字体          mtextpaint.settypeface(tf);                	//刷新重绘          if (mlayout != null) {              nulllayouts();              requestlayout();              invalidate();          }      }  }    //重载方法二  public void settypeface(@nullable typeface tf, @typeface.style int style) {    if (style > 0) {          if (tf == null) {              tf = typeface.defaultfromstyle(style);          } else {              tf = typeface.create(tf, style);          }  	//调用重载方法一,设置字体          settypeface(tf);        	//经过一些算法          int typefacestyle = tf != null ? tf.getstyle() : 0;          int need = style & ~typefacestyle;        	//打开画笔的粗体和斜体          mtextpaint.setfakeboldtext((need & typeface.bold) != 0);          mtextpaint.settextskewx((need & typeface.italic) != 0 ? -0.25f : 0);      } else {          mtextpaint.setfakeboldtext(false);          mtextpaint.settextskewx(0);          settypeface(tf);      }  }  

    分析下上述代码:

    重载方法一:

    textview 设置字体实际上就是操作 mtextpaint,mtextpaint 是 textpaint 的类对象,继承自 paint 即画笔,因此我们设置的字体实际上会通过调用画笔的方法来进行绘制

    重载方法二:

    相对于重载方法一,法二多传递了一个 textstyle 参数,主要用来标记粗体和斜体的:

    1)、如果设置了 textstyle ,进入第一个条件体,分情况:1、如果传进来的 tf 为 null ,则会根据传入的 style 去获取 typeface 字体,2、如果不为 null ,则会根据传入的 tf 和 style 去获取 typeface 字体。设置好字体后,接下来还会打开画笔的粗体和斜体设置

    2)、如果没有设置 textstyle,则只会设置字体,并把画笔的粗斜体设置置为 false 和 0

    从上述分析我们可以得知:textview 设置字体和字体样式最终都是通过画笔来完成的

    七、总结

    本篇文章主要讲了:

    1、android 字体大概的一个介绍

    2、关于影响 android 字体显示的三个属性

    3、textstyle,typeface,fontfamily 三者的一个关系

    4、设置的这三个属性是怎么实现这些效果的?

    可能大家会问,你上面那个需求还没讲怎么就要结束了呢?我上面那个需求,以今天所讲的知识可能还实现不了,别着急,关于 android 字体我准备写个系列,因为内容实在是太多了。这个系列文章不会让大家等太久,因为在参加掘金 6 月更文挑战,准备爆肝 9 篇😄

    好了,本篇文章到这里就结束了,如果有任何问题,欢迎给我留言,我们评论区一起讨论🤝

    以上就是android字体相关知识总结的详细内容,更多关于android字体的资料请关注<计算机技术网(www.ctvol.com)!!>其它相关文章!

    本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

    ctvol管理联系方式QQ:251552304

    本文章地址:https://www.ctvol.com/addevelopment/889205.html

    (0)
    上一篇 2021年10月19日
    下一篇 2021年10月19日

    精彩推荐