2012-03-21 67 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的依赖,但是你可以通过自己实现这个方法。

相关问题