No, sorry. You can only specify the built-in typefaces through XML.
Is there a way to do it from code in
one place, to say that the whole
application and all the components
should use the custom typeface instead
of the default one?
Not that I am aware of.
There are a variety of options for these nowadays:
Font resources and backports in the Android SDK, if you are using appcompat
Third-party libraries for those not using appcompat, though not all will support defining the font in layout resources
Although I am upvoting Manish's answer as the fastest and most targeted method, I have also seen naive solutions which just recursively iterate through a view hierarchy and update all elements' typefaces in turn. Something like this:
public static void applyFonts(final View v, Typeface fontToSet)
{
try {
if (v instanceof ViewGroup) {
ViewGroup vg = (ViewGroup) v;
for (int i = 0; i < vg.getChildCount(); i++) {
View child = vg.getChildAt(i);
applyFonts(child, fontToSet);
}
} else if (v instanceof TextView) {
((TextView)v).setTypeface(fontToSet);
}
} catch (Exception e) {
e.printStackTrace();
// ignore
}
}
You would need to call this function on your views both after inflating layout and in your Activity's onContentChanged() methods.
I like pospi's suggestion. Why not go all-out any use the 'tag' property of a view (which you can specify in XML - 'android:tag') to specify any additional styling that you can't do in XML. I like JSON so I'd use a JSON string to specify a key/value set. This class does the work - just call Style.setContentView(this, [resource id]) in your activity.
public class Style {
/**
* Style a single view.
*/
public static void apply(View v) {
if (v.getTag() != null) {
try {
JSONObject json = new JSONObject((String)v.getTag());
if (json.has("typeface") && v instanceof TextView) {
((TextView)v).setTypeface(Typeface.createFromAsset(v.getContext().getAssets(),
json.getString("typeface")));
}
}
catch (JSONException e) {
// Some views have a tag without it being explicitly set!
}
}
}
/**
* Style the passed view hierarchy.
*/
public static View applyTree(View v) {
apply(v);
if (v instanceof ViewGroup) {
ViewGroup g = (ViewGroup)v;
for (int i = 0; i < g.getChildCount(); i++) {
applyTree(g.getChildAt(i));
}
}
return v;
}
/**
* Inflate, style, and set the content view for the passed activity.
*/
public static void setContentView(Activity activity, int resource) {
activity.setContentView(applyTree(activity.getLayoutInflater().inflate(resource, null)));
}
}
Obviously you'd want to handle more than just the typeface to make using JSON worthwhile.
A benefit of the 'tag' property is that you can set it on a base style which you use as a theme and thus have it apply to all of your views automatically. EDIT: Doing this results in a crash during inflation on Android 4.0.3. You can still use a style and apply it to text views individually.
One thing you'll see in the code - some views have a tag without one being explicitly set - bizarrely it's the string 'Αποκοπή' - which is 'cut' in greek, according to google translate! What the hell...?
Absolutely possible.
Many ways to do it.
The fastest way, create condition with try - catch method..
try your certain font style condition, catch the error, and define the other font style.
I think there can be a handier way to do it. The following class will set a custom type face for all your the components of your application (with a setting per class).
/**
* Base Activity of our app hierarchy.
* @author SNI
*/
public class BaseActivity extends Activity {
private static final String FONT_LOG_CAT_TAG = "FONT";
private static final boolean ENABLE_FONT_LOGGING = false;
private Typeface helloTypeface;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
helloTypeface = Typeface.createFromAsset(getAssets(), "fonts/<your type face in assets/fonts folder>.ttf");
}
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
View view = super.onCreateView(name, context, attrs);
return setCustomTypeFaceIfNeeded(name, attrs, view);
}
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
View view = super.onCreateView(parent, name, context, attrs);
return setCustomTypeFaceIfNeeded(name, attrs, view);
}
protected View setCustomTypeFaceIfNeeded(String name, AttributeSet attrs, View view) {
View result = null;
if ("TextView".equals(name)) {
result = new TextView(this, attrs);
((TextView) result).setTypeface(helloTypeface);
}
if ("EditText".equals(name)) {
result = new EditText(this, attrs);
((EditText) result).setTypeface(helloTypeface);
}
if ("Button".equals(name)) {
result = new Button(this, attrs);
((Button) result).setTypeface(helloTypeface);
}
if (result == null) {
return view;
} else {
if (ENABLE_FONT_LOGGING) {
Log.v(FONT_LOG_CAT_TAG, "A type face was set on " + result.getId());
}
return result;
}
}
}
The default implementations of LayoutInflater do not support specifying the font typeface from xml. I have however seen it done in xml by providing a custom factory for the LayoutInflater that will parse such attributes from the xml tag.
The basic structure would like this.
public class TypefaceInflaterFactory implements LayoutInflater.Factory {
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
// CUSTOM CODE TO CREATE VIEW WITH TYPEFACE HERE
// RETURNING NULL HERE WILL TELL THE INFLATER TO USE THE
// DEFAULT MECHANISMS FOR INFLATING THE VIEW FROM THE XML
}
}
public class BaseActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LayoutInflater.from(this).setFactory(new TypefaceInflaterFactory());
}
}
This article provides a more in depth explanation of these mechanisms and how the author attempts to provide xml layout support for typefaces in this way. The code for the author's implementation can be found here.
Using pospi's suggestion and working with the 'tag' property like Richard did, i created a custom class that loads my custom fonts and applies them to the views according to their tags.
So basicly, instead of setting the TypeFace in the attribute android:fontFamily you are using the android:tag attritube and set it to one of the defined enums.
public class Fonts {
private AssetManager mngr;
public Fonts(Context context) {
mngr = context.getAssets();
}
private enum AssetTypefaces {
RobotoLight,
RobotoThin,
RobotoCondensedBold,
RobotoCondensedLight,
RobotoCondensedRegular
}
private Typeface getTypeface(AssetTypefaces font) {
Typeface tf = null;
switch (font) {
case RobotoLight:
tf = Typeface.createFromAsset(mngr,"fonts/Roboto-Light.ttf");
break;
case RobotoThin:
tf = Typeface.createFromAsset(mngr,"fonts/Roboto-Thin.ttf");
break;
case RobotoCondensedBold:
tf = Typeface.createFromAsset(mngr,"fonts/RobotoCondensed-Bold.ttf");
break;
case RobotoCondensedLight:
tf = Typeface.createFromAsset(mngr,"fonts/RobotoCondensed-Light.ttf");
break;
case RobotoCondensedRegular:
tf = Typeface.createFromAsset(mngr,"fonts/RobotoCondensed-Regular.ttf");
break;
default:
tf = Typeface.DEFAULT;
break;
}
return tf;
}
public void setupLayoutTypefaces(View v) {
try {
if (v instanceof ViewGroup) {
ViewGroup vg = (ViewGroup) v;
for (int i = 0; i < vg.getChildCount(); i++) {
View child = vg.getChildAt(i);
setupLayoutTypefaces(child);
}
} else if (v instanceof TextView) {
if (v.getTag().toString().equals(AssetTypefaces.RobotoLight.toString())){
((TextView)v).setTypeface(getTypeface(AssetTypefaces.RobotoLight));
}else if (v.getTag().toString().equals(AssetTypefaces.RobotoCondensedRegular.toString())) {
((TextView)v).setTypeface(getTypeface(AssetTypefaces.RobotoCondensedRegular));
}else if (v.getTag().toString().equals(AssetTypefaces.RobotoCondensedBold.toString())) {
((TextView)v).setTypeface(getTypeface(AssetTypefaces.RobotoCondensedBold));
}else if (v.getTag().toString().equals(AssetTypefaces.RobotoCondensedLight.toString())) {
((TextView)v).setTypeface(getTypeface(AssetTypefaces.RobotoCondensedLight));
}else if (v.getTag().toString().equals(AssetTypefaces.RobotoThin.toString())) {
((TextView)v).setTypeface(getTypeface(AssetTypefaces.RobotoThin));
}
}
} catch (Exception e) {
e.printStackTrace();
// ignore
}
}
}
In your Activity or Fragment you just call
Fonts fonts = new Fonts(getActivity());
fonts.setupLayoutTypefaces(mainLayout);
@majinboo's answer is revised for performance and memory management. Any more than one font need related Activity can use this Font class by giving the constructor itself as a parameter.
@Override
public void onCreate(Bundle savedInstanceState)
{
Font font = new Font(this);
}
It looks like using custom fonts has been made easy with Android O, you can basically use xml to achieve this. I have attached a link to Android official documentation for reference, and hopefully this will help people who still need this solution. Working with custom fonts in Android
Yes it is possible by overriding the default typeface.
I followed this solution and it worked like a charm for all TextViews and ActionBar text too with a single change.
public class MyApp extends Application {
@Override
public void onCreate() {
TypefaceUtil.overrideFont(getApplicationContext(), "SERIF", "fonts/Roboto-Regular.ttf"); // font from assets: "assets/fonts/Roboto-Regular.ttf
}
}
Instead of themes.xml as mentioned in the above link, I mentioned the default font to override in my styles.xml in my default app theme tag. The default typefaces that can be overwritten are serif, sans, monospace and normal.
TypefaceUtil.java
public class TypefaceUtil {
/**
* Using reflection to override default typeface
* NOTICE: DO NOT FORGET TO SET TYPEFACE FOR APP THEME AS DEFAULT TYPEFACE WHICH WILL BE OVERRIDDEN
* @param context to work with assets
* @param defaultFontNameToOverride for example "monospace"
* @param customFontFileNameInAssets file name of the font from assets
*/
public static void overrideFont(Context context, String defaultFontNameToOverride, String customFontFileNameInAssets) {
try {
final Typeface customFontTypeface = Typeface.createFromAsset(context.getAssets(), customFontFileNameInAssets);
final Field defaultFontTypefaceField = Typeface.class.getDeclaredField(defaultFontNameToOverride);
defaultFontTypefaceField.setAccessible(true);
defaultFontTypefaceField.set(null, customFontTypeface);
} catch (Exception e) {
Log.e("Can not set custom font " + customFontFileNameInAssets + " instead of " + defaultFontNameToOverride);
}
}
}
Initially, I didnt know the typefaces to be overwritten are fixed and set of defined values but eventually it helped me understand how Android deals with fonts and typefaces and their default values, which is a different point ofcourse.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentCustomView.bind(view)
binding.typeface = // some code to get user selected typeface
}
Now, if the user selects a new typeface you can just update the binding value and all your TextViews that you've set app:typeface will get updated.