2012-03-21 35 views
20

我想在我的Android應用程序中使用Roboto字體,並確保它適用於沒有安裝字體的早期版本的Android。我知道我可以通過使用Typeface.createFromAsset()來做到這一點,然後手動設置我的每個TextViews/Buttons/Other-Objects的字體。對於我在屏幕上顯示的每個對象執行此操作似乎都很痛苦。使用早期設備的Roboto字體

我的問題是,有沒有更好的方法來做到這一點?一些輔助類或在.xml主題文件中設置自定義字體的方法?任何自動化都會比手動列出每個屏幕上的每個對象並更改字體更好。

謝謝!

+0

你可以把它放在一個[風格](http://developer.android.com/guide/topics/ui/themes.html)也許。 Idk如果可以與資產的字體一起工作的話。 – zapl 2012-03-21 02:38:17

回答

16

檢索活動內的所有視圖,檢查其類型並應用適當的操作。

Typeface typeface = Typeface.createFromAsset(getAssets(), "fonts/Roboto/Roboto-Regular.ttf"); 
for (View view : allViews) 
{ 
if (view instanceof TextView) 
{ 
    TextView textView = (TextView) view; 
    textView.setTypeface(typeface); 
    } 
} 
30

以上公認的答案是正確的,但我只是想在此提供我的實現

我的實用工具類:

package com.example.utils; 

import android.content.Context; 
import android.graphics.Typeface; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.TextView; 

public class AndroidUtils 
{ 
    private static Typeface robotoTypeFace; 

    public static void setRobotoFont (Context context, View view) 
    { 
     if (robotoTypeFace == null) 
     { 
      robotoTypeFace = Typeface.createFromAsset(context.getAssets(), "fonts/Roboto/Roboto-Regular.ttf"); 
     } 
     setFont(view, robotoTypeFace); 
    } 

    private static void setFont (View view, Typeface robotoTypeFace) 
    { 
     if (view instanceof ViewGroup) 
     { 
      for (int i = 0; i < ((ViewGroup)view).getChildCount(); i++) 
      { 
       setFont(((ViewGroup)view).getChildAt(i), robotoTypeFace); 
      } 
     } 
     else if (view instanceof TextView) 
     { 
      ((TextView) view).setTypeface(robotoTypeFace); 
     } 
    } 
} 

如何使用它,假設this是一種活動:

AndroidUtils.setRobotoFont(this, view); 

要爲所有的TextView設置相同的字體,你可以使用de您的活動corView:

ViewGroup godfatherView = (ViewGroup)this.getWindow().getDecorView(); 
AndroidUtils.setRobotoFont(this, godfatherView); 

如果您有適配器或片段,不要忘記設置他們的字體。

另請參閱here也。

7

感謝@Jitsu,@Arnaud和@Pawan男,我做我的解決方案,比單獨所有的人更好:

/** 
* Adapted from http://stackoverflow.com/a/12387343/450148 
* 
* @author Anton Averin 
* @author Felipe Micaroni Lalli 
*/ 

package net.alouw.alouwCheckin.util; 

import android.content.Context; 
import android.graphics.Typeface; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.TextView; 

import java.util.EnumMap; 
import java.util.Map; 

public final class FontUtils { 
    private FontUtils() { 
    } 

    private enum FontType { 
     BOLD("fonts/Roboto/Roboto-BoldCondensed.ttf"), 
     BOLD_ITALIC("fonts/Roboto/Roboto-BoldCondensedItalic.ttf"), 
     NORMAL("fonts/Roboto/Roboto-Condensed.ttf"), 
     ITALIC("fonts/Roboto/Roboto-CondensedItalic.ttf"); 

     private final String path; 

     FontType(String path) { 
      this.path = path; 
     } 

     public String getPath() { 
      return path; 
     } 
    } 

    /* cache for loaded Roboto typefaces*/ 
    private static Map<FontType, Typeface> typefaceCache = new EnumMap<FontType, Typeface>(FontType.class); 

    /** 
    * Creates Roboto typeface and puts it into cache 
    */ 
    private static Typeface getRobotoTypeface(Context context, FontType fontType) { 
     String fontPath = fontType.getPath(); 

     if (!typefaceCache.containsKey(fontType)) { 
      typefaceCache.put(fontType, Typeface.createFromAsset(context.getAssets(), fontPath)); 
     } 

     return typefaceCache.get(fontType); 
    } 

    /** 
    * Gets roboto typeface according to passed typeface style settings. 
    * <p/> 
    * Will get Roboto-Bold for Typeface.BOLD etc 
    */ 
    private static Typeface getRobotoTypeface(Context context, Typeface originalTypeface) { 
     FontType robotoFontType = null; 

     if (originalTypeface == null) { 
      robotoFontType = FontType.NORMAL; 
     } else { 
      int style = originalTypeface.getStyle(); 

      switch (style) { 
       case Typeface.BOLD: 
        robotoFontType = FontType.BOLD; 
        break; 

       case Typeface.BOLD_ITALIC: 
        robotoFontType = FontType.BOLD_ITALIC; 
        break; 

       case Typeface.ITALIC: 
        robotoFontType = FontType.ITALIC; 
        break; 

       case Typeface.NORMAL: 
        robotoFontType = FontType.NORMAL; 
        break; 
      } 
     } 

     return (robotoFontType == null) ? originalTypeface : getRobotoTypeface(context, robotoFontType); 
    } 

    /** 
    * Walks ViewGroups, finds TextViews and applies Typefaces taking styling in consideration 
    * 
    * @param context - to reach assets 
    * @param view - root view to apply typeface to 
    */ 
    public static void setRobotoFont(Context context, View view) { 
     if (view instanceof ViewGroup) { 
      for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) { 
       setRobotoFont(context, ((ViewGroup) view).getChildAt(i)); 
      } 
     } else if (view instanceof TextView) { 
      Typeface currentTypeface = ((TextView) view).getTypeface(); 
      ((TextView) view).setTypeface(getRobotoTypeface(context, currentTypeface)); 
     } 
    } 
} 

而在你的onCreate主要活動的最後一件事:

if (Build.VERSION.SDK_INT < 11) { 
    ViewGroup godfatherView = (ViewGroup) this.getWindow().getDecorView(); 
    FontUtils.setRobotoFont(this, godfatherView); 
} 

而且在我的自定義列表視圖以上沒有工作的代碼,所以我不得不做出這樣的:

@Override 
public View getView(int position, View convertView, ViewGroup parent) { 
    // (...) 

    View view = // build your custom view here 

    if (Build.VERSION.SDK_INT < 11) { 
     FontUtils.setRobotoFont(activity, view); 
    } 

    return view; 
} 
+0

嗨,這是'(如果(Build.VERSION.SDK_INT <= 11))'否? – 2014-02-04 11:04:05

+0

不確定。如果是這種情況,請隨意編輯。 – 2014-08-15 21:16:25

3

我使用另一種解決方案。我將自定義LayoutInflater.Factory設置爲活動。所以在創建完成後我可以完全查看。我可以爲每個TextView安裝自定義字體,而無需在視圖層次結構上迭代。在您的所有應用程序中使用自定義字體應該做的一件事是在您的基本活動中調用new Font(...).install()。請參閱下面的示例。

這裏是我的解決方案與使用的示例:

import java.util.Map; 

import com.google.common.collect.Maps; 

import static com.google.common.base.Preconditions.checkNotNull; 

import android.R; 
import android.app.Activity; 
import android.content.Context; 
import android.content.res.TypedArray; 
import android.graphics.Typeface; 
import android.support.v4.app.FragmentActivity; 
import android.util.AttributeSet; 
import android.util.Log; 
import android.view.InflateException; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.widget.TextView; 

/** 
* Provides an ability to apply custom font to all {@link TextView} and subclasses. 
* 
* To install custom font use method {@link #install(Activity)} in {@link Activity#onCreate(android.os.Bundle)} 
* <b>before</b> calling super.onCreate(Bundle). 
* 
* <p/>Example of usage: 
* <pre> 
* {@code 
* public class BaseActivity extends SherlockFragmentActivity { 
* 
*  protected void onCreate(Bundle state) { 
*   applyCustomFontForPreICS(); 
*   super.onCreate(state); 
*  } 
* 
*  private void applyCustomFontForPreICS() { 
*   boolean isPreICS = Build.VERSION.SDK_INT < BUILD_VERSION_CODE_ICE_CREAM_SANDWICH 
*   if (isPreICS) { 
*    new Font(
*     "font/roboto_regular.ttf", 
*     "font/roboto_bold.ttf", 
*     "font/roboto_italic.ttf", 
*     "font/roboto_bold_italic.ttf" 
*    ).install(this); 
*   } 
*  } 
* } 
* } 
* </pre> 
* 
* @author Alexey Danilov ([email protected]) 
*/ 
public class Font { 

    private static final Map<String, Typeface> FONTS = Maps.newHashMap(); 

    private String regularFontPath; 
    private String boldFontPath; 
    private String italicFontPath; 
    private String boldItalicFontPath; 

    /** 
    * Creates instance to be used for setting particular font. 
    * 
    * @param regularPath regular font assets path, must be not {@code null} 
    * @param boldPath bold font assets path, must be not {@code null} 
    * @param italicPath italic font assets path, must be not {@code null} 
    * @param boldItalicPath bold and italic font assets path, must be not {@code null} 
    */ 
    public Font(String regularPath, String boldPath, String italicPath, String boldItalicPath) { 
     this.regularFontPath = checkNotNull(regularPath); 
     this.boldFontPath = checkNotNull(boldPath); 
     this.italicFontPath = checkNotNull(italicPath); 
     this.boldItalicFontPath = checkNotNull(boldItalicPath); 
    } 

    /** 
    * Installs custom font to activity. 
    * 
    * @param activity an activity custom font will be installed to, must be not {@code null}. 
    */ 
    public void install(Activity activity) { 
     checkNotNull(activity, "Activity must be not null!"); 

     LayoutInflater layoutInflater = activity.getLayoutInflater(); 
     boolean factoryIsEmpty = layoutInflater.getFactory() == null; 
     if (!factoryIsEmpty) { 
      throw new IllegalStateException("Impossible to use this method for this activity: layout factory is set!"); 
     } 
     layoutInflater.setFactory(new FontLayoutInflaterFactory()); 
    } 

    private Typeface getFont(int type, Context context) { 
     switch (type) { 
      case Typeface.NORMAL: 
       return getFont(context, regularFontPath); 
      case Typeface.BOLD: 
       return getFont(context, boldFontPath); 
      case Typeface.ITALIC: 
       return getFont(context, italicFontPath); 
      case Typeface.BOLD_ITALIC: 
       return getFont(context, boldItalicFontPath); 
      default: { 
       throw new IllegalArgumentException("Undefined font type " + type); 
      } 
     } 
    } 

    private Typeface getFont(Context context, String path) { 
     if (FONTS.containsKey(path)) { 
      return FONTS.get(path); 
     } else { 
      Typeface typeface = makeTypeface(context, path); 
      FONTS.put(path, typeface); 
      return typeface; 
     } 
    } 

    private Typeface makeTypeface(Context context, String path) { 
     try { 
      return Typeface.createFromAsset(context.getAssets(), path); 
     } catch (Exception e) { 
      // add user-friendly error message 
      throw new IllegalArgumentException(String.format("Error creating font from assets path '%s'", path), e); 
     } 
    } 

    private void applyFontToTextView(Context context, TextView textView, AttributeSet attrs) { 
     int[] fontStyleAttributes = {R.attr.textStyle}; 
     TypedArray typedArray = context.obtainStyledAttributes(attrs, fontStyleAttributes); 
     boolean isStyleSpecified = typedArray.getIndexCount() != 0; 
     int type = isStyleSpecified ? typedArray.getInt(0, Typeface.NORMAL) : Typeface.NORMAL; 
     Typeface font = getFont(type, context); 
     textView.setTypeface(font, type); 
    } 

    private final class FontLayoutInflaterFactory implements LayoutInflater.Factory { 

     // to improve perfomance the package with the most usable components should be the first. 
     private final String[] ANDROID_UI_COMPONENT_PACKAGES = { 
       "android.widget.", 
       "android.webkit.", 
       "android.view." 
     }; 

     @Override 
     public View onCreateView(String name, Context context, AttributeSet attrs) { 
      try { 
       // we install custom LayoutInflater.Factory, so FragmentActivity have no chance set own factory and 
       // inflate tag <fragment> in method onCreateView. So call it explicitly. 
       if ("fragment".equals(name) && context instanceof FragmentActivity) { 
        FragmentActivity fragmentActivity = (FragmentActivity) context; 
        return fragmentActivity.onCreateView(name, context, attrs); 
       } 

       View view = createView(name, attrs, LayoutInflater.from(context)); 
       if (view == null) { 
        // It's strange! The view is not ours neither android's. May be the package of this view 
        // is not listed in ANDROID_UI_COMPONENT_PACKAGES. Return null for the default behavior. 
        Log.d(LOG_TAG, "Cannot create view with name: " + name); 
        return null; 
       } 

       if (view instanceof TextView) { 
        TextView textView = (TextView) view; 
        applyFontToTextView(context, textView, attrs); 
       } 
       return view; 
      } catch (InflateException e) { 
       Log.e(LOG_TAG, "Error inflating view", e); 
       return null; 
      } catch (ClassNotFoundException e) { 
       Log.e(LOG_TAG, "Error inflating view", e); 
       return null; 
      } 
     } 

     private View createView(String name, AttributeSet attrs, LayoutInflater layoutInflater) throws ClassNotFoundException { 
      View view = null; 
      boolean isAndroidComponent = name.indexOf('.') == -1; 
      if (isAndroidComponent) { 
       // We don't know package name of the view with the given simple name. Try android ui packages listed in 
       // ANDROID_UI_COMPONENT_PACKAGES 

       // The same implementation is in the class PhoneLayoutInflater from internal API 
       for (String androidPackage : ANDROID_UI_COMPONENT_PACKAGES) { 
        try { 
         view = layoutInflater.createView(name, androidPackage, attrs); 
         if (view != null) { 
          break; 
         } 
        } catch (ClassNotFoundException e) { 
         // Do nothing, we will try another package 
        } 
       } 
      } else { 
       view = layoutInflater.createView(name, null, attrs); 
      } 
      return view; 
     } 
    } 
} 

注意其對guava的依賴,但是你可以通過自己實現這個方法。