Custom fonts and XML layouts (Android)

JavaAndroidUser InterfaceInterfaceFonts

Java Problem Overview


I'm trying to define a GUI layout using XML files in Android. As far as I can find out, there is no way to specify that your widgets should use a custom font (e.g. one you've placed in assets/font/) in XML files and you can only use the system installed fonts.

I know that, in the Java code, I could change the font of each widget manually using unique IDs. Alternatively, I could iterate over all the widgets in Java to make this change, but this would probably be very slow.

What other options do I have? Is there any better ways to making widgets that have a custom look? I don't particularly want to have to manually change the font for every new widget I add.

Java Solutions


Solution 1 - Java

You can extend TextView to set custom fonts as I learned here.

TextViewPlus.java:

package com.example;

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

public class TextViewPlus extends TextView {
    private static final String TAG = "TextView";

    public TextViewPlus(Context context) {
	    super(context);
    }

    public TextViewPlus(Context context, AttributeSet attrs) {
	    super(context, attrs);
	    setCustomFont(context, attrs);
    }

    public TextViewPlus(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setCustomFont(context, attrs);
    }

    private void setCustomFont(Context ctx, AttributeSet attrs) {
	    TypedArray a = ctx.obtainStyledAttributes(attrs, R.styleable.TextViewPlus);
	    String customFont = a.getString(R.styleable.TextViewPlus_customFont);
	    setCustomFont(ctx, customFont);
	    a.recycle();
    }

    public boolean setCustomFont(Context ctx, String asset) {
	    Typeface tf = null;
	    try {
	    tf = Typeface.createFromAsset(ctx.getAssets(), asset);  
	    } catch (Exception e) {
		    Log.e(TAG, "Could not get typeface: "+e.getMessage());
		    return false;
	    }

        setTypeface(tf);  
        return true;
    }

}

attrs.xml: (in res/values)

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

main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:foo="http://schemas.android.com/apk/res/com.example"
    android:orientation="vertical" android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <com.example.TextViewPlus
	    android:id="@+id/textViewPlus1"
	    android:layout_height="match_parent"
	    android:layout_width="match_parent"
	    android:text="@string/showingOffTheNewTypeface"
	    foo:customFont="saxmono.ttf">
    </com.example.TextViewPlus>
</LinearLayout>

You would put "saxmono.ttf" in the assets folder.

UPDATE 8/1/13

There are serious memory concerns with this method. See chedabob's comment below.

Solution 2 - Java

I'm 3 years late for the party :( However this could be useful for someone who might stumble upon this post.

I've written a library that caches Typefaces and also allow you to specify custom typefaces right from XML. You can find the library here.

Here is how your XML layout would look like, when you use it.

<com.mobsandgeeks.ui.TypefaceTextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/hello_world"
    geekui:customTypeface="fonts/custom_font.ttf" />

Solution 3 - Java

This might be a little late, but you need to create a singleton class that returns the custom typeface to avoid memory leaks.

TypeFace class:

public class OpenSans {

private static OpenSans instance;
private static Typeface typeface;

public static OpenSans getInstance(Context context) {
    synchronized (OpenSans.class) {
        if (instance == null) {
            instance = new OpenSans();
            typeface = Typeface.createFromAsset(context.getResources().getAssets(), "open_sans.ttf");
        }
        return instance;
    }
}

public Typeface getTypeFace() {
    return typeface;
}
}

Custom TextView:

public class NativelyCustomTextView extends TextView {

	public NativelyCustomTextView(Context context) {
		super(context);
		setTypeface(OpenSans.getInstance(context).getTypeFace());
	}

	public NativelyCustomTextView(Context context, AttributeSet attrs) {
		super(context, attrs);
		setTypeface(OpenSans.getInstance(context).getTypeFace());
	}

	public NativelyCustomTextView(Context context, AttributeSet attrs,
			int defStyle) {
		super(context, attrs, defStyle);
		setTypeface(OpenSans.getInstance(context).getTypeFace());
	}

}

By xml:

<com.yourpackage.views.NativelyCustomTextView
            android:id="@+id/natively_text_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_margin="20dp"
            android:text="@string/natively"
            android:textSize="30sp" /> 

Programmatically:

TextView programmaticallyTextView = (TextView) 
       findViewById(R.id.programmatically_text_view);

programmaticallyTextView.setTypeface(OpenSans.getInstance(this)
				.getTypeFace());

Solution 4 - Java

Old question, but I sure wish I read this answer here before I started my own search for a good solution. Calligraphy extends the android:fontFamily attribute to add support for custom fonts in your asset folder, like so:

<TextView 
  android:text="@string/hello_world"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:fontFamily="fonts/Roboto-Bold.ttf"/>

The only thing you have to do to activate it is attaching it to the Context of the Activity you're using:

@Override
protected void attachBaseContext(Context newBase) {
    super.attachBaseContext(new CalligraphyContextWrapper(newBase));
}

You can also specify your own custom attribute to replace android:fontFamily

It also works in themes, including the AppTheme.

Solution 5 - Java

Using DataBinding :

@BindingAdapter({"bind:font"})
public static void setFont(TextView textView, String fontName){
 textView.setTypeface(Typeface.createFromAsset(textView.getContext().getAssets(), "fonts/" + fontName));
}

In XML:

<TextView
app:font="@{`Source-Sans-Pro-Regular.ttf`}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

font file must be in assets/fonts/

Solution 6 - Java

If you only have one typeface you would like to add, and want less code to write, you can create a dedicated TextView for your specific font. See code below.

package com.yourpackage;
import android.content.Context;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.widget.TextView;

public class FontTextView extends TextView {
	public static Typeface FONT_NAME;


	public FontTextView(Context context) {
		super(context);
		if(FONT_NAME == null) FONT_NAME = Typeface.createFromAsset(context.getAssets(), "fonts/FontName.otf");
		this.setTypeface(FONT_NAME);
	}
	public FontTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    	if(FONT_NAME == null) FONT_NAME = Typeface.createFromAsset(context.getAssets(), "fonts/FontName.otf");
		this.setTypeface(FONT_NAME);
    }
    public FontTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    	if(FONT_NAME == null) FONT_NAME = Typeface.createFromAsset(context.getAssets(), "fonts/FontName.otf");
    	this.setTypeface(FONT_NAME);
    }
}

In main.xml, you can now add your textView like this:

<com.yourpackage.FontTextView
    android:id="@+id/tvTimer"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="" />

Solution 7 - Java

The best way to do it From Android O preview release is this way
1.)Right-click the res folder and go to New > Android resource directory. The New
Resource Directory window appears.
2.)In the Resource type list, select font, and then click OK.
3.)Add your font files in the font folder.The folder structure below generates R.font.dancing_script, R.font.la_la, and R.font.ba_ba.
4.)Double-click a font file to preview the file's fonts in the editor.

Next we must create a font family

1.)Right-click the font folder and go to New > Font resource file. The New Resource File window appears.
2.)Enter the file name, and then click OK. The new font resource XML opens in the editor.
3.)Enclose each font file, style, and weight attribute in the font tag element. The following XML illustrates adding font-related attributes in the font resource XML:

<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:android="http://schemas.android.com/apk/res/android">
    <font
    android:fontStyle="normal"
    android:fontWeight="400"
    android:font="@font/hey_regular" />
    <font
    android:fontStyle="italic"
    android:fontWeight="400"
    android:font="@font/hey_bababa" />
</font-family>

Adding fonts to a TextView:

   <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    **android:fontFamily="@font/ba_ba"**/>

As from the documentation

> Working With Fonts

all the steps are correct.

Solution 8 - Java

Extend TextView and give it a custom attribute or just use the android:tag attribute to pass in a String of what font you want to use. You will need to pick a convention and stick to it such as I will put all of my fonts in the res/assets/fonts/ folder so your TextView class knows where to find them. Then in your constructor you just set the font manually after the super call.

Solution 9 - Java

The only way to use custom fonts is through the source code.

Just remember that Android runs on devices with very limited resources and fonts might require a good amount of RAM. The built-in Droid fonts are specially made and, if you note, have many characters and decorations missing.

Solution 10 - Java

I might have a simple answer for the question without extending the TextView and implementing a long code.

Code :

 TextView tv = (TextView) findViewById(R.id.textview1);
	tv.setTypeface(Typeface.createFromAsset(getAssets(), "font.ttf"));

Place the custom font file in assets folder as usual and try this. It works for me. I just dont understand why peter has given such a huge code for this simple thing or he has given his answer in old version.

Solution 11 - Java

Also can be defined in the xml without creating custom classes

style.xml

<style name="ionicons" parent="android:TextAppearance">
    <!-- Custom Attr-->
    <item name="fontPath">fonts/ionicons.ttf</item>
</style>

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:app="http://schemas.android.com/apk/res-auto"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical" >
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="@style/ionicons"
        android:text=""/>
</LinearLayout>

A quick note, because I just always forgot where to put the fonts, its that the font must be inside assets and this folder resides in the same level that res and src, in my case its assets/fonts/ionicons.ttf

Updated Added root layout because this method needs xmlns:app="http://schemas.android.com/apk/res-auto" to work

Update 2 Forgot about a library that I've installed before called Calligraphy

Solution 12 - Java

Peter's answer is the best, but it can be improved by using the styles.xml from Android to customize your fonts for all textviews in your app.

My code is [here][1]

[1]: https://stackoverflow.com/questions/12281761/android-how-to-set-a-custom-font-for-whole-app/12282315#12282315 "here"

Solution 13 - Java

There are two ways to customize fonts :

!!! my custom font in assets/fonts/iran_sans.ttf

Way 1 : Refrection Typeface.class ||| best way

call FontsOverride.setDefaultFont() in class extends Application, This code will cause all software fonts to be changed, even Toasts fonts

AppController.java

public class AppController extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        //Initial Font
        FontsOverride.setDefaultFont(getApplicationContext(), "MONOSPACE", "fonts/iran_sans.ttf");

    }
}

FontsOverride.java

public class FontsOverride {

    public static void setDefaultFont(Context context, String staticTypefaceFieldName, String fontAssetName) {
        final Typeface regular = Typeface.createFromAsset(context.getAssets(), fontAssetName);
        replaceFont(staticTypefaceFieldName, regular);
    }

    private static void replaceFont(String staticTypefaceFieldName, final Typeface newTypeface) {
        try {
            final Field staticField = Typeface.class.getDeclaredField(staticTypefaceFieldName);
            staticField.setAccessible(true);
            staticField.set(null, newTypeface);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

Way 2: use setTypeface

for special view just call setTypeface() to change font.

CTextView.java

public class CTextView extends TextView {

    public CTextView(Context context) {
        super(context);
        init(context,null);
    }

    public CTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context,attrs);
    }

    public CTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context,attrs);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public CTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context,attrs);
    }

    public void init(Context context, @Nullable AttributeSet attrs) {

        if (isInEditMode())
            return;

        // use setTypeface for change font this view
        setTypeface(FontUtils.getTypeface("fonts/iran_sans.ttf"));

    }
}

FontUtils.java

public class FontUtils {

    private static Hashtable<String, Typeface> fontCache = new Hashtable<>();

    public static Typeface getTypeface(String fontName) {
        Typeface tf = fontCache.get(fontName);
        if (tf == null) {
            try {
                tf = Typeface.createFromAsset(AppController.getInstance().getApplicationContext().getAssets(), fontName);
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
            fontCache.put(fontName, tf);
        }
        return tf;
    }

}

Solution 14 - Java

Here's a tutorial that shows you how to setup a custom font like @peter described: http://responsiveandroid.com/2012/03/15/custom-fonts-in-android-widgets.html

it also has consideration for potential memory leaks ala http://code.google.com/p/android/issues/detail?id=9904 . Also in the tutorial is an example for setting a custom font on a button.

Solution 15 - Java

You can make easily custom textview class :-

So what you need to do first, make Custom textview class which extended with AppCompatTextView.

public class CustomTextView extends AppCompatTextView {
	private int mFont = FontUtils.FONTS_NORMAL;
	boolean fontApplied;

	public CustomTextView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		init(attrs, context);
	}

	public CustomTextView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(attrs, context);
	}

	public CustomTextView(Context context) {
		super(context);
		init(null, context);
	}

	protected void init(AttributeSet attrs, Context cxt) {
		if (!fontApplied) {
			if (attrs != null) {
				mFont = attrs.getAttributeIntValue(
						"http://schemas.android.com/apk/res-auto", "Lato-Regular.ttf",
						-1);
			}
			Typeface typeface = getTypeface();
			int typefaceStyle = Typeface.NORMAL;
			if (typeface != null) {
				typefaceStyle = typeface.getStyle();
			}
			if (mFont > FontUtils.FONTS) {
				typefaceStyle = mFont;
			}
			FontUtils.applyFont(this, typefaceStyle);
			fontApplied = true;
		}
	}
}

Now , every time Custom text view call and we will get int value from attribute int fontValue = attrs.getAttributeIntValue("http://schemas.android.com/apk/res-auto","Lato-Regular.ttf",-1).

Or

We can also get getTypeface() from view which we set in our xml (android:textStyle="bold|normal|italic"). So do what ever you want to do.

Now, we make FontUtils for set any .ttf font into our view.

public class FontUtils {

	public static final int FONTS = 1;
	public static final int FONTS_NORMAL = 2;
	public static final int FONTS_BOLD = 3;
	public static final int FONTS_BOLD1 = 4;

	private static Map<String, Typeface> TYPEFACE = new HashMap<String, Typeface>();

	static Typeface getFonts(Context context, String name) {
		Typeface typeface = TYPEFACE.get(name);
		if (typeface == null) {
			typeface = Typeface.createFromAsset(context.getAssets(), name);
			TYPEFACE.put(name, typeface);
		}
		return typeface;
	}

	public static void applyFont(TextView tv, int typefaceStyle) {

		Context cxt = tv.getContext();
		Typeface typeface;

		if(typefaceStyle == Typeface.BOLD_ITALIC) {
			typeface = FontUtils.getFonts(cxt, "FaktPro-Normal.ttf");
		}else if (typefaceStyle == Typeface.BOLD || typefaceStyle == SD_FONTS_BOLD|| typefaceStyle == FONTS_BOLD1) {
			typeface = FontUtils.getFonts(cxt, "FaktPro-SemiBold.ttf");
		} else if (typefaceStyle == Typeface.ITALIC) {
			typeface = FontUtils.getFonts(cxt, "FaktPro-Thin.ttf");
		} else {
			typeface = FontUtils.getFonts(cxt, "FaktPro-Normal.ttf");
		}
		if (typeface != null) {
			tv.setTypeface(typeface);
		}
	}
}

Solution 16 - Java

It may be useful to know that starting from Android 8.0 (API level 26) you can use a custom font in XML.

You can apply a custom font to the entire application in the following way.

  1. Put the font in the folder res/font.

  2. In res/values/styles.xml use it in the application theme. <style name="AppTheme" parent="{whatever you like}"> <item name="android:fontFamily">@font/myfont</item> </style>

Solution 17 - Java

Fontinator is an Android-Library make it easy, to use custom Fonts. https://github.com/svendvd/Fontinator

Solution 18 - Java

You can't extend TextView to create a widget or use one in a widgets layout: http://developer.android.com/guide/topics/appwidgets/index.html

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDrDefrostView Question on Stackoverflow
Solution 1 - JavapeterView Answer on Stackoverflow
Solution 2 - JavaRagunath JawaharView Answer on Stackoverflow
Solution 3 - JavaLeonardo CardosoView Answer on Stackoverflow
Solution 4 - JavathoutbeckersView Answer on Stackoverflow
Solution 5 - JavaCoolView Answer on Stackoverflow
Solution 6 - JavaTore RudbergView Answer on Stackoverflow
Solution 7 - Javajeevan sView Answer on Stackoverflow
Solution 8 - JavaNathan SchwermannView Answer on Stackoverflow
Solution 9 - JavaTareq ShaView Answer on Stackoverflow
Solution 10 - JavaSanView Answer on Stackoverflow
Solution 11 - Javanorman784View Answer on Stackoverflow
Solution 12 - JavaJelleView Answer on Stackoverflow
Solution 13 - JavaRasoul MiriView Answer on Stackoverflow
Solution 14 - JavabrowepView Answer on Stackoverflow
Solution 15 - JavadugguView Answer on Stackoverflow
Solution 16 - JavaVicView Answer on Stackoverflow
Solution 17 - JavaSven NählerView Answer on Stackoverflow
Solution 18 - JavaCarl WhalleyView Answer on Stackoverflow