package com.alexvasilkov.android.commons.texts; import android.app.Activity; import android.content.res.AssetManager; import android.graphics.Paint; import android.graphics.Typeface; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Fonts helper. Helps to manage custom fonts in application. * <p/> * First of all you're need to put your fonts into assets directory and define them as string resources (i.e. in <code>fonts.xml</code>): * <pre> * <resources> * <string name="font_arial_regular">fonts/Arial-Regular.ttf</string> * <string name="font_arial_bold">font:path/to/font/Arial-Bold.ttf</string> * </resources> * </pre> * <p/> * Note: this class will only use fonts under <code>fonts/</code> directory and fonts starting with <code>font:</code> preffix. * <p/> * Now you can use custom fonts in your XML layouts using <code>android:tag</code> attribute: * <pre> * <TextView * ... * android:tag="@string/font_arial_regular" /> * </pre> * <p/> * And last step you should call one of <code>Fonts.apply(...)</code> methods: * <p/> * {@link #apply(android.app.Activity)}<br/> * {@link #apply(android.view.View)}<br/> * {@link #apply(android.widget.TextView, int)}<br/> * {@link #apply(android.widget.TextView, String)} */ public final class Fonts { private static final Pattern FONT_PATTERN = Pattern.compile("^(?:font:)?(fonts/.*|(?<=font:).*)"); private static Map<String, Typeface> sFontsCacheMap = new HashMap<String, Typeface>(); /** * Applies fonts to all TextView views in Activity's window decor view.<br/> * TextView tag will be used to determine font. */ public static void apply(Activity activity) { ViewGroup root = (ViewGroup) activity.getWindow().getDecorView(); if (root.isInEditMode()) return; applyAllRecursively(root, activity.getAssets()); } /** * If view is instance of ViewGroup, than applies fonts to all TextView views in given ViewGroup<br/> * If view is instance of TextView, than applies font to provided TextView<br/> * TextView tag will be used to determine font. */ public static void apply(View view) { if (view.isInEditMode()) return; AssetManager assets = view.getContext().getAssets(); if (view instanceof TextView) { setTypeface((TextView) view, getFontFromTag(assets, view, false)); } else if (view instanceof ViewGroup) { applyAllRecursively((ViewGroup) view, assets); } } /** * Applies font to TextView<br/> * Note: this class will only accept fonts under <code>fonts/</code> directory and fonts starting with <code>font:</code> preffix. */ public static void apply(TextView textView, int fontStringId) { apply(textView, textView.getContext().getString(fontStringId)); } /** * Applies font to TextView<br/> * Note: this class will only accept fonts under <code>fonts/</code> directory and fonts starting with <code>font:</code> preffix. */ public static void apply(TextView textView, String fontPath) { if (textView.isInEditMode()) return; setTypeface(textView, getFontFromString(textView.getContext().getAssets(), fontPath, true)); } /* Internal methods */ private static void applyAllRecursively(ViewGroup viewGroup, AssetManager assets) { final int childCount = viewGroup.getChildCount(); for (int i = 0; i < childCount; i++) { View childView = viewGroup.getChildAt(i); if (childView instanceof TextView) { setTypeface((TextView) childView, assets, false); } else if (childView instanceof ViewGroup) { applyAllRecursively((ViewGroup) childView, assets); } } } private static void setTypeface(TextView textView, AssetManager assets, boolean strict) { setTypeface(textView, getFontFromTag(assets, textView, strict)); } private static void setTypeface(TextView textView, Typeface typeface) { if (typeface == null) return; // To not override previous typeface // Enabling sub-pixel rendering textView.setPaintFlags(textView.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG); textView.setTypeface(typeface); } static Typeface getTypeface(String fontPath, AssetManager assets) { return getFontFromString(assets, fontPath, true); } /* Helper methods */ private static Typeface getFontFromTag(AssetManager assets, View view, boolean strict) { Object tagObject = view.getTag(); String tag = tagObject instanceof String ? (String) tagObject : null; return getFontFromString(assets, tag, strict); } private static Typeface getFontFromString(AssetManager assets, String str, boolean strict) { Typeface font = sFontsCacheMap.get(str); if (font == null) { String path = getFontPathFromString(str, strict); if (path != null) { font = Typeface.createFromAsset(assets, path); sFontsCacheMap.put(str, font); } } return font; } private static String getFontPathFromString(String str, boolean strict) { if (str != null) { Matcher m = FONT_PATTERN.matcher(str); if (m.matches()) return m.group(1); } if (strict) throw new RuntimeException("Invalid font path: " + str); return null; } private Fonts() { } }