19 June, 2013

Custom fonts in Android (Part 3 of 3) - Memory leaks and FontsHelper

As reported here in issue 9904 there is a memory leak if we call Typeface.createFromAsset() multiple times as the fonts are kept open.
To avoid this problem and to keep from accessing assstes multiple times we'll create FontsHelper, a class that will contain all typefaces needed by the app.

public class FontsHelper {

 public static final String ROBOTO_LIGHT = "fonts/Roboto-Light.ttf";
 public static final String ROBOTO_BOLD = "fonts/Roboto-Bold.ttf";
 public static final String ROBOTO_CONDENSED = "fonts/Roboto-Condensed.ttf";
 
 private static Map fonts = new HashMap();
 
 public static Typeface getTypeFace(Context context, String fontPath) {
  
  if (!fonts.containsKey(fontPath)) {
   
   Typeface font = Typeface.createFromAsset(context.getAssets(), fontPath);
   fonts.put(fontPath, font);
  }
  
  return fonts.get(fontPath);
 }
 
 public static void setFont(View view, Typeface font) {
 
        if (view instanceof ViewGroup) 
  {
            for (int i = 0; i < ((ViewGroup)view).getChildCount(); i++) {
   
                setFont(((ViewGroup)view).getChildAt(i), font);
            }
        } else if (view instanceof TextView) {
  
            ((TextView) view).setTypeface(font);
        }
    }
 
 public static void setFont(Context ctx, TextView view, AttributeSet attrs) {

  TypedArray styleAttrs = context.obtainStyledAttributes(attrs, R.styleable.CustomFontView);
  String customFont = styleAttrs.getString(R.styleable.CustomFontView_customFont);
  setFont(context, view, customFont);
  styleAttrs.recycle();
 }

 public static boolean setFont(Context ctx, TextView view, String fontPath) {

  boolean successful = true;
 
  try {
  
   Typeface tf = AssetsHelper.getTypeFace(ctx, fontPath);
   view.setTypeface(tf);
  } catch (Exception e) {
   
   Log.e(TAG, "Error to get typeface: " + e.getMessage());
   successful = false;
  }

  return successful;
 }
This way we avoid the memory leaks and can further simplify the CustomFontTextView class from the previous post.

public class CustomFontTextView extends TextView {

 public CustomFontTextView(Context context, AttributeSet attrs) {

  super(context, attrs);
  FontsHelper.setFont(context, this, attrs);
 }

 public CustomFontTextView(Context context, AttributeSet attrs, int defStyle) {

  super(context, attrs, defStyle);
  FontsHelper.setFont(context, this, attrs);
 }
 
 public CustomFontTextView(Context context, String fontPath) {
  
  super(context);
  FontsHelper.setFont(context, this, fontPath);
 }
}
With the custom font static variables in FontsHelper and the constructor with a fontPath argument in CustomFontTextView we can also apply the font by code in an easier way.