Use Roboto font for earlier devices

时间秒杀一切 提交于 2019-12-20 08:07:38

问题


I would like to use the Roboto font in my Android application and make sure it works for earlier versions of Android that don't have the font installed. I know I can do this by using Typeface.createFromAsset() and then manually setting the font for each of my TextViews/Buttons/Other-Objects. It seems like a big pain to do this for every object I show on the screen though.

My question is, is there a better way to do this? Some helper class or a way to set a custom font in a .xml theme file? Anything automated would be better than manually listing out every object on each screen and changing the font.

Thanks!


回答1:


Retrieve all views inside activity, check its type and apply appropriate action.

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



回答2:


Above accepted answer is correct but I just wanted to supply my implementation here

My utility class:

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

How to use it, assuming this is an Activity:

AndroidUtils.setRobotoFont(this, view);

To set the same font to all the TextView you can use the decorView of your activity:

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

If you have adapters or fragments, don't forget to set their font as well.

See here also.




回答3:


Thanks to @Jitsu, @Arnaud and @Pawan M, I made my solution, better than all of them alone:

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

And the last thing in your onCreate main activity:

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

And in my lists with custom views the code above didn't work, so I had to make this:

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



回答4:


Here is my improved version of this solution. It can cache fonts and takes into consideration TextView.textStyle parameter settings. So it can set bold text. http://anton.averin.pro/2012/09/12/how-to-use-android-roboto-font-in-honeycomb-and-earlier-versions/




回答5:


I use another solution. I set custom LayoutInflater.Factory to activity. So I have full access to view after it creating. I can install custom font for every TextView without iterating on view hierarchy. One thing you should do to use custom font in all your application is call new Font(...).install() in your base activity. See exaple below.

Here is my solution with sample of usage:

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

Note it has dependency on guava, but you can implement this methods by himself.




回答6:


The best sollution ever is Calligraphy



来源:https://stackoverflow.com/questions/9797872/use-roboto-font-for-earlier-devices

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!