martedì 29 settembre 2015

Creating Android TextView with custom font

This article demonstrates how to extend a TextView to use a custom font. We will add to the standard TextView an attribute font, that will be accessible either from xml or programmatically.

Create a new Android project and create in the values folder the attributes.xml file, in order to define the attribute font (this will be used in xml configuration).

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="TextViewCustomFont">
        <attr name="font" format="string" />
    </declare-styleable>
</resources>

Then we need to extend the TextView class with our customization:

package com.devsourcenter;

import android.content.Context;
import android.content.res.AssetManager;
import android.content.res.TypedArray;
import android.graphics.Typeface;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.widget.TextView;

import com.devsourcenter.textviewwithcustomfont.R;

public class TextViewCustomFont extends TextView {

   public TextViewCustomFont(Context context, AttributeSet attributes) {
      super(context, attributes);
      // attributes variable contains all the attributes defined in the xml file

      // typedArray contains only the attribute specified by the resource TextViewCustomFont
      TypedArray typedArray = context.obtainStyledAttributes(attributes,
            R.styleable.TextViewCustomFont);
      // get the font attribute from xml
      String fontPath = typedArray
            .getString(R.styleable.TextViewCustomFont_font);

      // if the attribute is present it is set as font
      if (fontPath != null) {
         AssetManager assetManager = context.getResources().getAssets();
         // this is the object related to the font
         Typeface typeface = Typeface
               .createFromAsset(assetManager, fontPath);
         // now the assign the font to the TextView
         setTypeface(typeface);
         typedArray.recycle();
      }
   }

   /**
    * Assign the font in fontPath to the TextView
    * 
    * @param fontPath
    *            Path of the font file in assets folder
    */
   public void setFont(@Nullable String fontPath) {
      AssetManager assetManager = getContext().getResources().getAssets();
      Typeface typeface = Typeface.createFromAsset(assetManager, fontPath);
      setTypeface(typeface);
   }
}

The setFont method is used to set the font programmatically, while the constructor checks if the font is specified in the xml file. Now we can include TextViewCustomFont in our activity layout (activity_main.xml):

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    xmlns:app="http://schemas.android.com/apk/res/com.devsourcenter.textviewwithcustomfont"
    tools:context="com.devsourcenter.textviewwithcustomfont.MainActivity" >

    <!-- we set the font in xml with attribute font -->
    
    <com.devsourcenter.TextViewCustomFont
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:font="fonts/palatino.ttf"
        android:text="@string/palatino"
        android:textSize="40sp" />

    <!-- in this view we set the font programmatically -->
    
    <com.devsourcenter.TextViewCustomFont
        android:id="@+id/courier_new_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/courier_new" 
        android:textSize="40sp" />
        
</LinearLayout>

In the first TextView we set the font with the xml custom attribute font (we declared the namespace for the prefix app to use the attribute), otherwise the font of the second TextView is changed directly in the Activity:

package com.devsourcenter.textviewwithcustomfont;

import com.devsourcenter.TextViewCustomFont;

import android.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity {

   @Override
   protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      
      TextViewCustomFont customTextView = 
            (TextViewCustomFont) findViewById(R.id.courier_new_text_view);
      // set font programmatically
      customTextView.setFont("fonts/courierNew.ttf");
   }

}

As shown above we put the fonts courierNew.ttf and palatino.ttf in the assets/fonts folder. The running activity looks like this: