【问题标题】:Use Roboto font for earlier devices为早期设备使用 Roboto 字体
【发布时间】:2012-03-21 02:35:30
【问题描述】:

我想在我的 Android 应用程序中使用 Roboto 字体,并确保它适用于未安装该字体的早期版本的 Android。我知道我可以通过使用 Typeface.createFromAsset() 然后为我的每个 TextViews/Buttons/Other-Objects 手动设置字体来做到这一点。不过,对我在屏幕上显示的每个对象都执行此操作似乎很痛苦。

我的问题是,有没有更好的方法来做到这一点?一些帮助类或在 .xml 主题文件中设置自定义字体的方法?任何自动化都比手动列出每个屏幕上的每个对象并更改字体要好。

谢谢!

【问题讨论】:

  • 你可以把它放在style 中。 Idk 如果这适用于资产中的字体。

标签: android android-layout android-fonts


【解决方案1】:

以上接受的答案是正确的,但我只是想在这里提供我的实现

我的实用类:

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是一个Activity:

AndroidUtils.setRobotoFont(this, view);

要为所有 TextView 设置相同的字体,您可以使用活动的 decorView:

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

如果您有适配器或片段,请不要忘记设置它们的字体。

另见here

【讨论】:

    【解决方案2】:

    检索活动中的所有视图,检查其类型并应用适当的操作。

    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);
      }
    }
    

    【讨论】:

      【解决方案3】:

      感谢@Jitsu、@Arnaud 和@Pawan M,我提出了我的解决方案,比他们一个人都好:

      /**
       * 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;
      }
      

      【讨论】:

      • 嗨,你认为是(if (Build.VERSION.SDK_INT &lt;= 11)) 不是吗?
      【解决方案4】:

      这是我对此解决方案的改进版本。 它可以缓存字体并考虑 TextView.textStyle 参数设置。所以它可以设置粗体文本。 http://anton.averin.pro/2012/09/12/how-to-use-android-roboto-font-in-honeycomb-and-earlier-versions/

      【讨论】:

      • 非常好!使用 > 是完美的。
      【解决方案5】:

      我使用另一种解决方案。我将自定义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 (danikula@gmail.com)
       */
      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,但是你可以自己实现这个方法。

      【讨论】:

        【解决方案6】:

        有史以来最好的解决方案是Calligraphy

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-11-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-11-08
          相关资源
          最近更新 更多