Custom Font (Typeface) ChoTextView, EditText , Button Trong Android

Android phát hành phiên bản Ice Cream Sandwich với Typeface (fonts) mặc định được gọi là Roboto, theo nhận xét của tôi đó là kiểu chữ đẹp và hấp dẫn. Trong quá trình xây dựng và phát triển ứng dụng cho khách hàng, rất nhiều ứng dụng yêu cầu và đòi hỏi phải custom một kiểu chữ riêng theo yêu cầu design của Khách Hàng. Mục đích của bài viết này sẽ trình bày làm sao để custom một Typeface (fonts) cho những View (TextView, Button, EditText) trong ứng dụng của bạn.

Folder Assets Nơi bạn đặt những custome font của bạn là trong Folder Assets và được load bởi phương thức Typeface.createFromAsset(,). **Android API **cho phép chúng ta load 2 loại font với đuôi mở rộng là .otf and .ttf . Tôi đặt những Typeface của tôi nhưng trong hình bên dưới, để cho dễ nhìn hơn tôi đã đặt tiếp trong một subfolder.

Screenshot 2016-01-24 21.21.39.png

Ngoài ra thất thoát Bộ Nhớ (Memory Leaks) Việc load Typeface từ folder assets mỗi lần bạn muốn sử dụng nó sẽ làm cho ứng dụng của bạn thất thoát 1 phần bộ nhớ không cần thiết.Để giải quyết vấn đề này, Tôi tạo một class mới, gọi là TypefaceCache.java như bên dưới:

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.widget.TextView;
import java.util.Hashtable;

/**
 * @Author:HoaLQ History:1/21/16.
 */
public class TypefaceCache {

    public static final int VARIATION_NORMAL = 0;
    public static final int VARIATION_LIGHT = 1;
    public static final int VARIATION_SPECIAL = 2;
    public static final int VARIATION_THIN = 3;
    public static final int VARIATION_MEDIUM = 4;

    public static final int VARIATION_CONDENSED = 5;
    public static final int VARIATION_BLACK = 6;
    public static final int VARIATION_EXTRA_LIGHT = 7;
    public static final int VARIATION_SEMI = 8;
    public static final int VARIATION_BOLD = 9;

    protected static final Hashtable<String, Typeface> mTypefaceCache = new Hashtable<>();

    public static Typeface getTypeface(Context context) {
            return getTypeface(context, Typeface.NORMAL, VARIATION_NORMAL);
    }

    public static Typeface getTypeface(Context context, int fontStyle, int variation) {
        if (context == null) {
            return null;
        }

        String typefaceName = "Roboto-Regular.ttf";
        if (variation == VARIATION_BLACK) {
            switch (fontStyle) {
                case Typeface.ITALIC: {
                    typefaceName = "Roboto-BlackItalic.ttf";
                    break;
                }
                default: {
                    typefaceName = "Roboto-Black.ttf";
                    break;
                }
            }
        } else if (variation == VARIATION_BOLD) {
            switch (fontStyle) {
                case Typeface.ITALIC: {
                    typefaceName = "Roboto-BoldItalic.ttf";
                    break;
                }
                default: {
                    typefaceName = "Roboto-Bold.ttf";
                    break;
                }
            }
        } else if (variation == VARIATION_NORMAL) {
            switch (fontStyle) {
                case Typeface.ITALIC: {
                    typefaceName = "Roboto-Italic.ttf";
                    break;
                }
                default: {
                    typefaceName = "Roboto-Regular.ttf";
                    break;
                }
            }
        } else if (variation == VARIATION_LIGHT) {
            switch (fontStyle) {
                case Typeface.ITALIC: {
                    typefaceName = "Roboto-LightItalic.ttf";
                    break;
                }
                default: {
                    typefaceName = "Roboto-Light.ttf";
                    break;
                }
            }
        } else if (variation == VARIATION_MEDIUM) {
            switch (fontStyle) {
                case Typeface.ITALIC: {
                    typefaceName = "Roboto-MediumItalic.ttf";
                    break;
                }
                default: {
                    typefaceName = "Roboto-Medium.ttf";
                    break;
                }
            }
        } else if (variation == VARIATION_THIN) {
            switch (fontStyle) {
                case Typeface.ITALIC: {
                    typefaceName = "Roboto-ThinItalic.ttf";
                    break;
                }
                default: {
                    typefaceName = "Roboto-Thin.ttf";
                    break;
                }
            }
        } else if (variation == VARIATION_CONDENSED) {
            switch (fontStyle) {
                case Typeface.BOLD: {
                    typefaceName = "RobotoCondensed-Bold.ttf";
                    break;
                }
                case Typeface.BOLD_ITALIC: {
                    typefaceName = "RobotoCondensed-BoldItalic.ttf";
                    break;
                }
                case Typeface.ITALIC: {
                    typefaceName = "RobotoCondensed-Italic.ttf";
                    break;
                }
                case Typeface.NORMAL: {
                    typefaceName = "RobotoCondensed-Regular.ttf";
                    break;
                }
                default: {
                    typefaceName = "RobotoCondensed-Regular.ttf";
                    break;
                }
            }
        } else if (variation == VARIATION_EXTRA_LIGHT) {
            switch (fontStyle) {
                case Typeface.ITALIC: {
                    typefaceName = "RobotoCondensed-LightItalic.ttf";
                    break;
                }
                default: {
                    typefaceName = "RobotoCondensed-Light.ttf";
                    break;
                }
            }
        }

        return getTypefaceForTypefaceName(context, typefaceName);

    }

    protected static Typeface getTypefaceForTypefaceName(Context context, String typefaceName) {
        if (!mTypefaceCache.containsKey(typefaceName)) {
            Typeface typeface = Typeface.createFromAsset(context.getApplicationContext().getAssets(), "fonts/" + typefaceName);
            if (typeface != null) {
                mTypefaceCache.put(typefaceName, typeface);
            }
        }
        return mTypefaceCache.get(typefaceName);
    }

    public static void setCustomTypeface(Context context, TextView view, AttributeSet attributeSet) {
        if (context == null || view == null) {
            return;
        }

        // Skip at design-time (edit mode)
        if (view.isInEditMode()) {
            return;
        }

        // read custom fontVariation from attributes , default to normal
        int variation = TypefaceCache.VARIATION_NORMAL;
        if (attributeSet != null) {
            TypedArray array = context.getTheme().obtainStyledAttributes(attributeSet, R.styleable.OpenTextView, 0, 0);
            if (array != null) {
                try {
                    variation = array.getInteger(R.styleable.OpenTextView_fontVariation, TypefaceCache.VARIATION_NORMAL);
                } finally {
                    array.recycle();
                }
            }
        }

        final int fontStype;
        if (view.getTypeface() != null) {
            boolean isBold = view.getTypeface().isBold();
            boolean isItalic = view.getTypeface().isItalic();
            if (isBold && isItalic) {
                fontStype = Typeface.BOLD_ITALIC;
            } else if (isBold) {
                fontStype = Typeface.BOLD;
            } else if (isItalic) {
                fontStype = Typeface.ITALIC;
            } else {
                fontStype = Typeface.NORMAL;
            }
        } else {
            fontStype = Typeface.NORMAL;
        }

        Typeface typeface = getTypeface(context, fontStype, variation);
        if (typeface != null) {
            view.setTypeface(typeface);
        }
    }
}

Mục đích của chúng ta là tạo các Custom View (TextView, Button, EditText) sử dụng các font mà chúng ta mong muốn. Vì vậy tôi đã tạo ra một phương thưc trong class TypefaceCache.java như bên dưới:

 public static void setCustomTypeface(Context context, TextView view, AttributeSet attributeSet) {
        if (context == null || view == null) {
            return;
        }

        // Skip at design-time (edit mode)
        if (view.isInEditMode()) {
            return;
        }

        // read custom fontVariation from attributes , default to normal
        int variation = TypefaceCache.VARIATION_NORMAL;
        if (attributeSet != null) {
            TypedArray array = context.getTheme().obtainStyledAttributes(attributeSet, R.styleable.OpenTextView, 0, 0);
            if (array != null) {
                try {
                    variation = array.getInteger(R.styleable.OpenTextView_fontVariation, TypefaceCache.VARIATION_NORMAL);
                } finally {
                    array.recycle();
                }
            }
        }

        final int fontStype;
        if (view.getTypeface() != null) {
            boolean isBold = view.getTypeface().isBold();
            boolean isItalic = view.getTypeface().isItalic();
            if (isBold && isItalic) {
                fontStype = Typeface.BOLD_ITALIC;
            } else if (isBold) {
                fontStype = Typeface.BOLD;
            } else if (isItalic) {
                fontStype = Typeface.ITALIC;
            } else {
                fontStype = Typeface.NORMAL;
            }
        } else {
            fontStype = Typeface.NORMAL;
        }

        Typeface typeface = getTypeface(context, fontStype, variation);
        if (typeface != null) {
            view.setTypeface(typeface);
        }
    }

Ngoài ra chúng ta phải tạo 1 file attrs.xlm với nội dung như bên dưới:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="OpenTextView">
        <attr name="fontVariation" format="enum">
            <enum name="normal" value="0"/>
            <enum name="light" value="1"/>
            <enum name="special" value="2"/>
            <enum name="thin" value="3"/>
            <enum name="medium" value="4"/>
            <enum name="condensed" value="5"/>
        </attr>
    </declare-styleable>
</resources>

Bây giờ là lúc chúng ta sẽ tạo các View chúng ta mong muốn sử dụng fonts mới chúng ta tạo. Trong ví dụ này tôi sẽ custom 3 view mà chúng ta hay dùng nhất trong bất kỳ project Android nào: Trước tiên tôi sẽ custom TextView, Tôi tạo 1 class mới gọi là OpenTextView extends từ TextView như bên dưới:

public class OpenTextView extends TextView {

    private static final String TAG = LogUtils.makeLogTag(OpenTextView.class);

    public OpenTextView(Context context) {
        super(context);
        TypefaceCache.setCustomTypeface(context, this, null);
    }

    public OpenTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypefaceCache.setCustomTypeface(context, this, attrs);
    }

    public OpenTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypefaceCache.setCustomTypeface(context, this, attrs);
    }
}

Tương tự như trên tôi cũng tạo ra các Custome View cho Button và EditText : OpenEditText.java

public class OpenEditText extends EditText {
    public OpenEditText(Context context) {
        super(context);
        TypefaceCache.setCustomTypeface(context, this, null);
    }

    public OpenEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypefaceCache.setCustomTypeface(context, this, attrs);
    }

    public OpenEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypefaceCache.setCustomTypeface(context, this, attrs);
    }
}

OpenButton.java

public class OpenButton extends Button {

    public boolean mFixWindowWordEnabled = false;

    public OpenButton(Context context) {
        super(context);
        TypefaceCache.setCustomTypeface(context, this, null);
    }

    public OpenButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypefaceCache.setCustomTypeface(context, this, attrs);
    }

    public OpenButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypefaceCache.setCustomTypeface(context, this, attrs);
    }
 }