12/08/2018, 16:09

Add custom font for TextView in Android

Khi làm việc với các ứng dụng Android thì chắc bạn không còn xa lạ gì với TextView. Nó được sử dụng rất là phổ biết, được ta dùng khi muốn hiển thị bất cứ dòng text đầy yêu thương nào đó. Đối với các version Android về sau thì càng nhiều font chữ được hỗ trợ có sẵn, nhưng không phải của nào cũng ...

Khi làm việc với các ứng dụng Android thì chắc bạn không còn xa lạ gì với TextView. Nó được sử dụng rất là phổ biết, được ta dùng khi muốn hiển thị bất cứ dòng text đầy yêu thương nào đó. Đối với các version Android về sau thì càng nhiều font chữ được hỗ trợ có sẵn, nhưng không phải của nào cũng có. Khi bạn muốn những dòng chữ của mình có thể bay lượn như rồng phượng, bạn phải sử dụng một font chữ nào đó. Khi đó bạn cần nghĩ tới một Custom TextView để hiển thị được font chữ của bạn. Vậy để làm thế nào để có một custom TextView hiển thị bất cứ font nào mong muốn? Sau đây mình sẽ giới thiệu cách để add một custom font mong muốn cho TextView.

Đầu tiên muốn gì thì muốn bạn phải có font chữ của mình cái đã rồi mới tính tiếp. Khởi tạo package /main/assets/fonts để chứa đựng các font của bạn tại đây.

Tiếp đó là khai báo Attribute cho CustomTextView của mình, ở đây ta sẽ tạo một attr có name="fonts" vì mình muốn thay font thôi mà

<?xml version="1.0" encoding="utf-8"?>
<resources>
// CustomTextView is your class
    <declare-styleable name="CustomTextView">
        <attr name="fonts" format="string" />
    </declare-styleable>
</resources>

Có fonts, có attrs rồi, tạo CustomTextView thôi, nó sẽ extends từ TextView

class CustomTextView: TextView {

    constructor(context: Context): super(context)

    constructor(context: Context, attrs: AttributeSet?): super(context, attrs) {
        init(attrs)
    }

    constructor(context: Context, attrs: AttributeSet?, defStyle: Int): super(context, attrs, defStyle) {
        init(attrs)
    }

    private fun init(attrs: AttributeSet?) {
        includeFontPadding = false
        if (attrs != null) {
            val typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomTextView)
            val fontName = typedArray.getString(R.styleable.CustomTextView_fonts)

            try {
                if (fontName != null) {
                    val typeface = Typeface.createFromAsset(context.assets, "fonts/$fontName")
                    setTypeface(typeface)
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }
            typedArray.recycle()
        }
    }

}

Lưu ý: khi mình làm việc với một số font thì nhận thấy đối với một số font nó có padding mặc định, và nhiều khi padding này rất là dày nên làm layout bị sai lệch nhiều khi không biết tại sao mình ko set padding cho TextView mà nó vẫn cứ đẩy nhau, nên như ở trên mình đã set includeFontPadding = false để không sử dụng padding của font.

Như vậy về cơ bản là ta đã tạo được một CustomTextView để hiển thị những font chữ mong muốn rồi. Việc còn lại là sử dụng ra sao cho đúng cách thôi.

<com.framgia.intelligence.ui.CustomTextView
        android:layout_awidth="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="8dp"
        android:textSize="20sp"
        android:textColor="@color/black"
        android:text="ユーザー認証"
        app:fonts="NotoSansCJKjp-Bold.otf" />

Như vậy mình đã giới thiệu cách để add một font mong muốn trong TextView. Tuy nhiên đây chưa phải là tất cả =))

Sau đây mình sẽ cho bạn xem memory của device khi sử dụng TextView mặc định và khi sử dụng CustomTextView đối với TextView thì:

không có gì lạ lắm, mọi việc đều bình thường, còn đây là hình ảnh khi sử dụng CustomTextView

như ta thấy, xài nhiều hơn có, CPU đỏ rực :v memory đã bị sử dụng nhiều hơn mức cho phép vì vậy không chỉ dừng lại ở đây bạn sẽ thấy ứng dụng của bạn bị dừng ngay lập tức "force stop" kèm thêm

Out of memory

Đối với những device hịn hịn thì bạn có thể ít khi gặp hiện tượng này, có khi chả thấy bao giờ vì máy hịn quá, nhưng đối với những thiết bị thấp hơn thì bạn có thể sẽ gặp hiện tượng này, tỉ lệ xảy ra tỉ lệ thuận với độ cùi của thiết bị =))

Vậy tại sao lại vậy?

Đó là do Typeface.createFromAsset(context.assets, "fonts/$fontName") được thực hiện liên tục mỗi khi một CustomTextView được khởi tạo, Typeface được tạo ra liên tục, việc đọc vào assets cũng vậy. Đối với các custom font có size nhỏ thì hiện tượng có thể ít xảy ra, memory vẫn có thể tải được, nhưng đối với những custom font có size lên tới hàng chục mb thì đó lại là một vấn đề.

Vậy làm sao phải khắc phục hiện tượng này?

Để có thể khắc phục hiện tượng này, thay vì bạn liên tục tạo ra các Typeface mới thì bạn chỉ tạo ra 1 Typeface tương ứng với 1 font, khi nào cần dùng thì lấy ra sử dụng chứ không tạo mới. Chúng ta cần tạo fontCache để lưu trữ các typeface, như vậy bạn mới có thể ngừng hành động tạo mới và đọc vào assets liên tục.

companion object {
        private val fontCache: MutableMap<String, Typeface> = mutableMapOf()

        fun getFontCache(context: Context, fontName: String): Typeface? {
            var typeface = fontCache[fontName]
            if (typeface == null) {
                try {
                    typeface = Typeface.createFromAsset(context.assets, "fonts/$fontName")
                    fontCache.put(fontName, typeface)
                } catch (e: Exception) {
                    return null
                }
            }
            return typeface
        }
    }

Sau đó thay vì:

private fun init(attrs: AttributeSet?) {
        ...
            try {
                if (fontName != null) {
                    val typeface = Typeface.createFromAsset(context.assets, "fonts/$fontName")
                    setTypeface(typeface)
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }
           ...
        }
    }

ta chỉ việc gọi các typceface đã lưu trữ:

private fun init(attrs: AttributeSet?) {
        ...
            if (fontName != null) {
                val typeface = BaseTextView.getFontCache(context, fontName)
                if (typeface != null) setTypeface(typeface)
            }
            ...
        }
    }

Như vậy vấn đề được giải quyết, ứng dụng lại nhẹ nhàng chạy.

Trên đây là hướng dẫn tạo một CustomTextView sử dụng custom font mong muốn, và vấn đề gặp phải - cách giải quyết vấn đề đó. Mong nó có thể giúp ích cho bạn!

0