Android使用自定義字體


情景

你需要為整個應用替換自定義字體。

解決方案

1)Android默認方法 #1

你可以通過ID查找到View,然后挨個為它們設置字體。在單個View的情況下,它看起來也沒有那么可怕。

1 Typeface customFont = Typeface.createFromAsset(this.getAssets(), "fonts/YourCustomFont.ttf");
2 TextView view = (TextView) findViewById(R.id.activity_main_header);
3 view.setTypeface(customFont);

但是在很多TextView、Button等文本組件的情況下,我敢肯定你不會喜歡這個方法的。:D

2)Android默認方法 #2

你可以為每個文本組件創建一個子類,如TextView、Button等,然后在構造函數中加載自定義字體。

 1 public class BrandTextView extends TextView {
 2 
 3       public BrandTextView(Context context, AttributeSet attrs, int defStyle) {
 4           super(context, attrs, defStyle);
 5       }
 6      public BrandTextView(Context context, AttributeSet attrs) {
 7           super(context, attrs);
 8       }
 9      public BrandTextView(Context context) {
10           super(context);
11      }
12      public void setTypeface(Typeface tf, int style) {
13            if (style == Typeface.BOLD) {
14                 super.setTypeface(Typeface.createFromAsset(getContext().getAssets(), "fonts/YourCustomFont_Bold.ttf"));
15             } else {
16                super.setTypeface(Typeface.createFromAsset(getContext().getAssets(), "fonts/YourCustomFont.ttf"));
17             }
18       }
19  }

然后只需要將標准的文本控件替換成你自定義的就可以了(例如BrandTextView替換TextView)。

1 <com.your.package.BrandTextView
2          android:layout_width="wrap_content"
3          android:layout_height="wrap_content"
4          android:text="View with custom font"/>
5 <com.your.package.BrandTextView
6          android:layout_width="wrap_content"
7          android:layout_height="wrap_content"
8          android:textStyle="bold"
9          android:text="View with custom font and bold typeface"/>

還有,你甚至可以直接在XML中添加自定義的字體屬性。要實現這個,你需要定義你自己的declare-styleable屬性,然后在組件的構造函數中解析它們。

為了不占篇幅介紹這么基礎的東西,這里有一篇不錯的文章告訴你怎么自定義控件屬性。

http://kevindion.com/2011/01/custom-xml-attributes-for-android-widgets/

在大多數情況下,這個方法還不賴,並且有一些優點(例如,切換字體粗細等等,字體可以在組件xml文件的typeface屬性中定義)。但是我認為這個實現方法還是太重量級了,並且依賴大量的模板代碼,為了一個替換字體的簡單任務,有點兒得不償失。

3)我的解決方案

理想的解決方案是自定義主題,然后應用到全局或者某個Activity。
但不幸的是,Android的android:typeface屬性只能用來設置系統內嵌的字體,而非用戶自定義字體(例如assets文件中的字體)。這就是為什么我們無法避免在Java代碼中加載並設置字體。

所以我決定創建一個幫助類,使得這個操作盡可能的簡單。使用方法:

FontHelper.applyFont(context, findViewById(R.id.activity_root), "fonts/YourCustomFont.ttf");

並且這行代碼會用來加載所有的基於TextView的文本組件(TextView、Button、RadioButton、ToggleButton等等),而無需考慮界面的布局層級如何。

標准(左)與自定義(右)字體的用法。

 

這是怎么做到的?非常簡單:

 

 1 public static void applyFont(final Context context, final View root, final String fontName) {
 2     try {
 3         if (root instanceof ViewGroup) {
 4             ViewGroup viewGroup = (ViewGroup) root;
 5             for (int i = 0; i < viewGroup.getChildCount(); i++)
 6                 applyFont(context, viewGroup.getChildAt(i), fontName);
 7         } else if (root instanceof TextView)
 8             ((TextView) root).setTypeface(Typeface.createFromAsset(context.getAssets(), fontName));
 9     } catch (Exception e) {
10         Log.e(TAG, String.format("Error occured when trying to apply %s font for %s view", fontName, root));
11         e.printStackTrace();
12     }
13 }

正如你所看到的,所需要做的僅僅是將基於TextView的文本組件從布局中遍歷出來而已。

你可以在這里下載到示例代碼,里面有FontHelper的具體用法。

一些想法

在多個項目中,我都碰到過類似的需求,早期采用的是第二種實現方法,但是缺點在於對於第三方組件,你需要去修改別人的代碼,才能實現自定義字體,這恰恰違反了OC(Open & Close)原則,對擴展開放,對修改封閉。

剛看到第三種的時候,也是驚為天人,姑且不說結果,我覺得這種活躍的思路非常重要,很值得學習參考。

但是最后被team里的人否決了,理由是違背組件設計原則,實現方式略嫌粗暴。后來我仔細想想,一個是要做好內存管理(似乎會引起內存問題),視圖狀態改變,也要重復加載(橫豎屏、onResume等),也絕對不是簡單的活兒。

所以暫定使用第一種方法,typeface使用單例,需要時設置字體。

我個人覺得第一種還是個體力活,而且到后來,這個代碼重復率還是非常高的,這又違背了DRY原則。

在地鐵上的時候,突然想到DI(Dependency Inject)。已經有一些DI的框架,如ButterKnife,那寫出來應該是這樣:

@CustomFont(R.id.textView) TextView textView

1 @InjectView(id=R.id.textView, customFont=true) View anyView
2 @InjectView(id=R.id.textView, customFont=true, customFont="fonts/ltfz.ttf") View anyView

這樣寫出來代碼相比重復寫setTypeface要好一些。

目前我們的項目還沒有使用這類DI框架,等以后引入了,使用第二種注入,寫起來應該是很爽的。

參考

https://segmentfault.com/q/1010000000494116


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM