Android 对早期设备使用Roboto字体

Android 对早期设备使用Roboto字体,android,android-layout,android-fonts,Android,Android Layout,Android Fonts,我想在我的Android应用程序中使用Roboto字体,并确保它适用于没有安装该字体的Android早期版本。我知道我可以通过使用Typeface.createFromAsset()然后手动设置每个文本视图/按钮/其他对象的字体来实现这一点。对于我在屏幕上显示的每一个对象来说,这样做似乎是一件非常痛苦的事情 我的问题是,有没有更好的办法?某个助手类或在.xml主题文件中设置自定义字体的方法?任何自动化操作都比手动列出每个屏幕上的每个对象并更改字体要好 谢谢 检索活动内的所有视图,检查其类型并应用

我想在我的Android应用程序中使用Roboto字体,并确保它适用于没有安装该字体的Android早期版本。我知道我可以通过使用Typeface.createFromAsset()然后手动设置每个文本视图/按钮/其他对象的字体来实现这一点。对于我在屏幕上显示的每一个对象来说,这样做似乎是一件非常痛苦的事情

我的问题是,有没有更好的办法?某个助手类或在.xml主题文件中设置自定义字体的方法?任何自动化操作都比手动列出每个屏幕上的每个对象并更改字体要好


谢谢

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

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

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

我的实用程序类:

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);
        }
    }
}
要为所有文本视图设置相同的字体,可以使用活动的decorView:

ViewGroup godfatherView = (ViewGroup)this.getWindow().getDecorView();
AndroidUtils.setRobotoFont(this, godfatherView);
if (Build.VERSION.SDK_INT < 11) {
    ViewGroup godfatherView = (ViewGroup) this.getWindow().getDecorView();
    FontUtils.setRobotoFont(this, godfatherView);
}
如果您有适配器或片段,不要忘记设置它们的字体


另请参见。

这是我对该解决方案的改进版本。 它可以缓存字体并考虑TextView.textStyle参数设置。因此,它可以设置粗体文本。

多亏了@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));
        }
    }
}
/**
*改编自http://stackoverflow.com/a/12387343/450148
*
*@作者Anton Averin
*@作者Felipe Micaroni Lalli
*/
包net.alouw.alouwCheckin.util;
导入android.content.Context;
导入android.graphics.Typeface;
导入android.view.view;
导入android.view.ViewGroup;
导入android.widget.TextView;
导入java.util.EnumMap;
导入java.util.Map;
公共最终类FontUtils{
私有方图蒂尔斯(){
}
私有枚举类型{
粗体(“字体/Roboto/Roboto.ttf”),
粗体斜体(“字体/Roboto/Roboto-boldcondenceditalic.ttf”),
普通(“字体/Roboto/Roboto Condensed.ttf”),
斜体(“fonts/Roboto/Roboto condenceditalic.ttf”);
私有最终字符串路径;
FontType(字符串路径){
this.path=path;
}
公共字符串getPath(){
返回路径;
}
}
/*已加载Roboto字体的缓存*/
私有静态映射typefaceCache=新的EnumMap(FontType.class);
/**
*创建Roboto字体并将其放入缓存
*/
私有静态字体getRobotoTypeface(上下文上下文,FontType FontType){
字符串fontPath=fontType.getPath();
如果(!typefaceCache.containsKey(fontType)){
typefaceCache.put(fontType,Typeface.createFromAsset(context.getAssets(),fontPath));
}
返回typefaceCache.get(fontType);
}
/**
*根据传递的字体样式设置获取roboto字体。
*

*将获得Roboto粗体字体。粗体等 */ 私有静态字体getRobotoTypeface(上下文上下文,字体原始字体){ FontType robotoFontType=null; if(originalTypeface==null){ robotoFontType=FontType.NORMAL; }否则{ int style=originalTypeface.getStyle(); 开关(样式){ 大小写字体。粗体: robotoFontType=FontType.BOLD; 打破 大小写字体。粗体\斜体: robotoFontType=FontType.BOLD_ITALIC; 打破 case Typeface.ITALIC: robotoFontType=FontType.ITALIC; 打破 case Typeface.NORMAL: robotoFontType=FontType.NORMAL; 打破 } } 返回(robotoFontType==null)?originalTypeface:getRobotoTypeface(上下文,robotoFontType); } /** *浏览视图组,查找文本视图并应用字体,同时考虑样式 * *@param context-访问资源 *@param view-要应用字体的根视图 */ 公共静态void setRobotoFont(上下文、视图){ if(视图组的视图实例){ 对于(int i=0;i<((视图组)视图)。getChildCount();i++){ setRobotoFont(context,((ViewGroup)视图).getChildAt(i)); } }else if(查看文本视图的实例){ 字体currentTypeface=((TextView)视图).getTypeface(); 设置字体(getRobotoTypeface(context,currentTypeface)); } } }

onCreate主要活动中的最后一件事:

ViewGroup godfatherView = (ViewGroup)this.getWindow().getDecorView();
AndroidUtils.setRobotoFont(this, godfatherView);
if (Build.VERSION.SDK_INT < 11) {
    ViewGroup godfatherView = (ViewGroup) this.getWindow().getDecorView();
    FontUtils.setRobotoFont(this, godfatherView);
}
if(Build.VERSION.SDK\u INT<11){
ViewGroup godfatherView=(ViewGroup)this.getWindow().getDecorView();
setRobotoFont(这个,教父视图);
}
在我的自定义视图列表中,上面的代码不起作用,因此我不得不这样做:

@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;
}
@覆盖
公共视图getView(int位置、视图转换视图、视图组父视图){
// (...)
视图=//在此处构建自定义视图
if(Build.VERSION.SDK_INT<11){
setRobotoFont(活动、视图);
}
返回视图;
}

我使用另一种解决方案。我将自定义设置为活动。因此,在创建后,我可以完全访问视图。我可以为每个TextView安装自定义字体,而无需迭代视图层次结构。要在所有应用程序中使用自定义字体,需要做的一件事是在基本活动中调用
新字体(…).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;
        }
    }
}
import java.util.Map;
导入com.google.common.collect.Maps;
导入静态com.google.common.base.premissions.checkNotNull;
导入android.R;
导入android.app.Activity;
导入android.content.Context;
导入android.content.res.TypedArray;
导入android.graphics.Typeface;
导入android.support.v4.app.FragmentActivity;
导入android.util.AttributeSet;
导入android.util.Log;
导入android.view.InflateException;
导入android.view.LayoutInflater;
导入android。