package com.norbsoft.typefacehelper; import android.app.Activity; import android.content.Context; import android.graphics.Paint; import android.graphics.Typeface; import android.text.Spannable; import android.text.SpannableString; import android.text.TextPaint; import android.text.style.MetricAffectingSpan; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; /** * Helper class for setting custom typefaces to {@link android.view.View} and * {@link android.view.ViewGroup}. Supports String styling using {@link android.text.SpannableString} * * Applies typeface to all child views recursively. * * For detailed information and guide: https://github.com/jskierbi/android-typeface-helper * * @author Jakub Skierbiszewski */ public class TypefaceHelper { private static TypefaceCollection sDefaultTypefaceCollection; /** * Initialize helper with typeface collection. * This should be called inside {@link android.app.Application#onCreate()} * @param typefaceCollection */ public static void init(TypefaceCollection typefaceCollection) { sDefaultTypefaceCollection = typefaceCollection; } /** * Return spannable string with default typeface style (style: Typeface.NORMAL) * see: http://stackoverflow.com/questions/8607707/how-to-set-a-custom-font-in-the-actionbar-title * * @param context to obtain string resource * @param strResId string resource id, content * * @throws IllegalStateException {@link #init(TypefaceCollection)} has not been called * @return SpannableString that can be used in TextView.setText() method */ public static SpannableString typeface(Context context, int strResId) { if (sDefaultTypefaceCollection == null) { throw new IllegalStateException("Default typeface collection not initialized. Forgot to call init()?"); } return typeface(context.getString(strResId), sDefaultTypefaceCollection, Typeface.NORMAL); } /** * Return spannable string with typeface in certain style * see: http://stackoverflow.com/questions/8607707/how-to-set-a-custom-font-in-the-actionbar-title * * @param context to obtain string resource * @param strResId string resource id, content * @param style Typeface.NORMAL, Typeface.BOLD, Typeface.ITALIC or Typeface.BOLD_ITALIC * * @throws IllegalStateException {@link #init(TypefaceCollection)} has not been calledC * @return SpannableString that can be used in TextView.setText() method */ public static SpannableString typeface(Context context, int strResId, int style) { if (sDefaultTypefaceCollection == null) { throw new IllegalStateException("Default typeface collection not initialized. Forgot to call init()?"); } return typeface(context.getString(strResId), sDefaultTypefaceCollection, style); } /** * Return spannable string with typeface in certain style * see: http://stackoverflow.com/questions/8607707/how-to-set-a-custom-font-in-the-actionbar-title * * @param context to obtain string resource * @param strResId string resource id, content * @param collection TypefaceCollection instance * @param style Typeface.NORMAL, Typeface.BOLD, Typeface.ITALIC or Typeface.BOLD_ITALIC * * @return SpannableString that can be used in TextView.setText() method */ public static SpannableString typeface(Context context, int strResId, TypefaceCollection collection, int style) { return typeface(context.getString(strResId), collection, style); } /** * Return spannable string with default typeface style (style: Typeface.NORMAL) * see: http://stackoverflow.com/questions/8607707/how-to-set-a-custom-font-in-the-actionbar-title * * @param sequence to typeface typeface to * * @throws IllegalStateException {@link #init(TypefaceCollection)} has not been called * @return SpannableString that can be used in TextView.setText() method */ public static SpannableString typeface(CharSequence sequence) { if (sDefaultTypefaceCollection == null) { throw new IllegalStateException("Default typeface collection not initialized. Forgot to call init()?"); } return typeface(sequence, sDefaultTypefaceCollection, Typeface.NORMAL); } /** * Return spannable string with applied typeface in certain style * see: http://stackoverflow.com/questions/8607707/how-to-set-a-custom-font-in-the-actionbar-title * * @param sequence content * @param typefaceCollection Collection of typefaces * @param style Typeface.NORMAL, Typeface.BOLD, Typeface.ITALIC or Typeface.BOLD_ITALIC * @throws IllegalArgumentException when invalid style is passed * @return SpannableString that can be used in TextView.setText() method */ public static SpannableString typeface(CharSequence sequence, TypefaceCollection typefaceCollection, int style) { checkTypefaceStyleThrowing(style); SpannableString s = new SpannableString(sequence); s.setSpan(new TypefaceSpan(typefaceCollection.getTypeface(style)), 0, s.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); return s; } /** * Return spannable string with default typeface style (style: Typeface.NORMAL) * see: http://stackoverflow.com/questions/8607707/how-to-set-a-custom-font-in-the-actionbar-title * * @param sequence content * @param typefaceCollection Collection of typefaces * @return SpannableString that can be used in TextView.setText() method */ public static SpannableString typeface(CharSequence sequence, TypefaceCollection typefaceCollection) { return typeface(sequence, typefaceCollection, Typeface.NORMAL); } /** * Apply typefaces to main acitivty view (android.R.id.content). * @throws IllegalStateException {@link #init(TypefaceCollection)} has not been called * @param activity to typeface custom typefaces to */ public static void typeface(Activity activity) { if (sDefaultTypefaceCollection == null) { throw new IllegalStateException("Default typeface collection not initialized. Forgot to call init()?"); } typeface(activity.findViewById(android.R.id.content), sDefaultTypefaceCollection); } /** * Apply typefaces to view * @throws IllegalStateException {@link #init(TypefaceCollection)} has not been called * @param view to typeface custom typefaces to */ public static void typeface(View view) { if (sDefaultTypefaceCollection == null) { throw new IllegalStateException("Default typeface collection not initialized. Forgot to call init()?"); } typeface(view, sDefaultTypefaceCollection); } /** * Apply typefaces to main acitivty view (android.R.id.content). * @param activity to typeface custom typefaces to * @param typefaceCollection to obtain typefaces SparseArray. */ public static void typeface(Activity activity, TypefaceCollection typefaceCollection) { typeface(activity.findViewById(android.R.id.content), typefaceCollection); } /** * Apply fonts from map to all children of view (or view itself) * * @param view view for which to typeface fonts * @param typefaceCollection Collection of typefaces */ public static void typeface(View view, TypefaceCollection typefaceCollection) { if (view instanceof ViewGroup) { applyTypeface((ViewGroup) view, typefaceCollection); } else { applyForView(view, typefaceCollection); } } /** * Check if typeface style int is one of: * <ul> * <li>{@link android.graphics.Typeface#NORMAL}</li> * <li>{@link android.graphics.Typeface#BOLD}</li> * <li>{@link android.graphics.Typeface#ITALIC}</li> * <li>{@link android.graphics.Typeface#BOLD_ITALIC}</li> * </ul> * @param style */ private static void checkTypefaceStyleThrowing(int style) { switch (style) { case Typeface.NORMAL: case Typeface.BOLD: case Typeface.ITALIC: case Typeface.BOLD_ITALIC: break; default: throw new IllegalArgumentException("Style have to be in (Typeface.NORMAL, Typeface.BOLD, Typeface.ITALIC, Typeface.BOLD_ITALIC)"); } } /** * Apply typeface to all ViewGroup childs * * @param viewGroup to typeface typeface * @param typefaceCollection typeface collection */ private static void applyTypeface(ViewGroup viewGroup, TypefaceCollection typefaceCollection) { for (int i = 0; i < viewGroup.getChildCount(); i++) { View childView = viewGroup.getChildAt(i); if (childView instanceof ViewGroup) { applyTypeface((ViewGroup) childView, typefaceCollection); } else { applyForView(childView, typefaceCollection); } } } /** * Apply typeface to single view * * @param view to typeface typeface * @param typefaceCollection typeface collection */ private static void applyForView(View view, TypefaceCollection typefaceCollection) { if (view instanceof TextView) { TextView textView = (TextView) view; Typeface oldTypeface = textView.getTypeface(); final int style = oldTypeface == null ? Typeface.NORMAL : oldTypeface.getStyle(); textView.setTypeface(typefaceCollection.getTypeface(style)); textView.setPaintFlags(textView.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG); } } private static class TypefaceSpan extends MetricAffectingSpan { Typeface typeface; TypefaceSpan(Typeface typeface) { this.typeface = typeface; } @Override public void updateMeasureState(TextPaint p) { p.setTypeface(typeface); p.setFlags(p.getFlags() | Paint.SUBPIXEL_TEXT_FLAG); } @Override public void updateDrawState(TextPaint tp) { tp.setTypeface(typeface); tp.setFlags(tp.getFlags() | Paint.SUBPIXEL_TEXT_FLAG); } } }