Android 性能优化:字体 (为自定义字体提供字体内存缓存)
Android 上自定义字体的代码一般如下:
TextView textview = (TextView) findViewById(R.id.your_referenced_textview); // adjust this line to get the TextView you want to change Typeface typeface = Typeface.createFromAsset(getAssets(),"SourceSansPro-Regular.ttf"); // create a typeface from the raw ttf textview.setTypeface(typeface); // apply the typeface to the textview
然后就结束了,如果想要改变一个Textview的字体就是这么简单,最好的情况就是上面的代码在
如果你只想用在单个实例上那么这种方法是足够的,但是如果你想要给app中成千上万的view都使用自定义字体的话,这可能就不是一个好方法了,毕竟我们不可能在每个初始化的地方都去加上上面那一段
onCreate()
方法中进行调用。如果你只想用在单个实例上那么这种方法是足够的,但是如果你想要给app中成千上万的view都使用自定义字体的话,这可能就不是一个好方法了,毕竟我们不可能在每个初始化的地方都去加上上面那一段
提供字体内存缓存
虽然Android现在已经很流畅,但是我们依然应该考虑优化性能。所以,我们应该把自定义的字体缓存起来,这样就不用每次去初始化,在 britzl on stackoverflow上有一个比较好的答案。
public class FontCache { private static HashMap<String, Typeface> fontCache = new HashMap<>(); public static Typeface getTypeface(String fontname, Context context) { Typeface typeface = fontCache.get(fontname); if (typeface == null) { try { typeface = Typeface.createFromAsset(context.getAssets(), fontname); } catch (Exception e) { return null; } fontCache.put(fontname, typeface); } return typeface; } }
缓存下字体就能让我们不用一直去操作 Assets
文件夹,接下来就能实现一个继承自 TextView
的类。
继承TextView
首先来创建一个类继承自 TextView,这样就能在 XML 中来使用,它继承了 TextView所有的属性和功能,然后再加上自定义的字体。
public class EatFoodyTextView extends TextView { public EatFoodyTextView(Context context) { super(context); applyCustomFont(context); } public EatFoodyTextView(Context context, AttributeSet attrs) { super(context, attrs); applyCustomFont(context); } public EatFoodyTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); applyCustomFont(context); } private void applyCustomFont(Context context) { Typeface customFont = FontCache.getTypeface("SourceSansPro-Regular.ttf", context); setTypeface(customFont); } }
开始的三个都是构造函数,里面都调用了 applyCustomFont()
方法,然后从上面的 FontCache
类中拿到缓存的字体文件,这样就不用每个view都去重复的从 Assets中取字体,节约了资源,最后将取到的字体设置到 setTypeface()
中。
使用自定义类
现在我们只需要在XML中直接使用,不需要再写其他的java代码,
<RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <com.futurestudio.foody.views.EatFoodyTextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@color/eat_foody_green_dark" android:textSize="20sp" android:text="Future Studio Blog" android:layout_marginBottom="24dp"/> </RelativeLayout>
我们可以依然使用TextView的其他属性,(textSize,textColor之类的),只需要把
但是使用中会发现,虽然一些TextView的属性比如
<TextView/>
替换成 <com.futurestudio.foody.views.EatFoodyTextView/>
,这个前面的是全部的包名,然后就会自己应用字体。但是使用中会发现,虽然一些TextView的属性比如
textSize
能正常的显示,但是 textStyle
这个属性并不能正常的生效。添加每个ttf文件
首先将同一个系列的三种样式的 ttf
文件都加到 assets
中
在XML中使用textStyle属性
在前面已经讲解了自定义view的使用
<io.futurestud.tutorials.customfont.CustomFontTextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="12dp" android:text="http://futurestud.io/blog/" android:textSize="18sp" android:textStyle="bold"/>
一般情况下,我们更希望使用的是标准的
提示:如果你对使用
android:textStyle
而不是用一个自定义的属性 customfont:textStyle
。但是像上面这样的属性直接放上去肯定是不能生效的,还需要再加一些代码才行。提示:如果你对使用
customfont:textStyle
的方式比较感兴趣,那么下一篇文章中会介绍如何使用。实现CustomFontTextView
为了能继续的使用系统的 android:textStyle
属性,需要一些步骤。
首先需要在代码拿到这个属性的信息,这只需要一行代码:
int textStyle = attrs.getAttributeIntValue(ANDROID_SCHEMA, "textStyle", Typeface.NORMAL);
attr
这个值是来自 TextView
的第二个构造函数中的参数,我们可以使用这个对象的 getAttributeIntValue()
方法获取XML的属性。先看一下上面代码中的
ANDROID_SCHEMA
这个参数,这个是一个常量,定义在XML的最顶部中(xmlns:android="http://schemas.android.com/apk/res/android" ),第二个参数就是定义的属性名,最后一个参数是默认值,如果这个属性没有设置,那么就会选择 Typeface.NORMAL
。当我们考虑了样式之后,完善一下代码,全部的代码看起来就像下面这样。
public class CustomFontTextView extends TextView { public static final String ANDROID_SCHEMA = "http://schemas.android.com/apk/res/android"; public CustomFontTextView(Context context, AttributeSet attrs) { super(context, attrs); applyCustomFont(context, attrs); } public CustomFontTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); applyCustomFont(context, attrs); } private void applyCustomFont(Context context, AttributeSet attrs) { int textStyle = attrs.getAttributeIntValue(ANDROID_SCHEMA, "textStyle", Typeface.NORMAL); Typeface customFont = selectTypeface(context, textStyle); setTypeface(customFont); } private Typeface selectTypeface(Context context, int textStyle) { /* * information about the TextView textStyle: * http://developer.android.com/reference/android/R.styleable.html#TextView_textStyle */ switch (textStyle) { case Typeface.BOLD: // bold return FontCache.getTypeface("SourceSansPro-Bold.ttf", context); case Typeface.ITALIC: // italic return FontCache.getTypeface("SourceSansPro-Italic.ttf", context); case Typeface.BOLD_ITALIC: // bold italic return FontCache.getTypeface("SourceSansPro-BoldItalic.ttf", context); case Typeface.NORMAL: // regular default: return FontCache.getTypeface("SourceSansPro-Regular.ttf", context); } }
这样我们的自定义字体就能使用标准的应用字体样式 textStyle
。
看看成果
首先我们在XML中写一些布局,包括原生的 Roboto 字体以及不同形式的自定义字体。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="12dp" android:text="http://futurestud.io/blog/" android:textSize="18sp"/> <io.futurestud.tutorials.customfont.CustomFontTextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="12dp" android:text="http://futurestud.io/blog/" android:textSize="18sp"/> <io.futurestud.tutorials.customfont.CustomFontTextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="12dp" android:text="http://futurestud.io/blog/" android:textSize="18sp" android:textStyle="bold"/> <io.futurestud.tutorials.customfont.CustomFontTextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="12dp" android:text="http://futurestud.io/blog/" android:textSize="18sp" android:textStyle="italic"/> </LinearLayout>
这个文件在模拟器上显示的效果就像下面这样。