excilys/androidannotations — Issue #18: @Font to load a typeface on a TextView
Author: pyricau
Created: 2012-01-06 10:52:10+00:00
State: open
Labels: Enhancement

Original Issue here: http://code.google.com/p/androidannotations/issues/detail?id=106

Reported by project member py.ricau, Sep 13, 2011
Principle :

Let's say you have a MyFont.otf file in your asset directory.

You could then do the following :

@Font("MyFont.otf")
@ViewById
TextView myTextView;

=> The generated code would do the following :

Typeface myFont = Typeface.createFromAsset(getAssets(), "MyFont.otf");
myTextView.setTypeface(myFont);

Maybe it should also work when not using @ViewById, ie :

@Font("MyFont.otf")
TextView myTextView;

=> The user injects myTextView manually.

@Font will do it's injection after view injection.

If twice the same font is injected, then we should load it only once (based on the string value of the annotation).

Maybe we should also check that the font file exists in the asset folder (or not ?)

Delete comment Comment 1 by project member mathieu.debrito, Sep 14, 2011
That's a really good idea !
Also, developers often use fonts on all views in an activity.

Could be cool to have it on the activty
like @EActivity([layout, .font?])

or @EActivity(layout)
@Font(font)
Delete comment Comment 2 by project member py.ricau, Sep 14, 2011
Interesting idea. However, there is no way to know all the textviews in a layout, appart from reading the layout file and its included templates.

However, we could to this :

@EActvitiy(R.layout.main)
@Font(font = "MyFont.otf", textviews = { R.id.tv1, R.id.tv2 })
public class MyActivity extends Activity {
Delete comment Comment 3 by project member py.ricau, Sep 14, 2011
In fact, I like it better then the initial idea, because then the user won't use a ViewById just to set the font. Plus, it makes it easier to centralise font loading.

Users may want to set multiple fonts, and there can only be one annotation of a given type at a time.

So maybe there could be two ways to set fonts :

@EActivity(R.layout.main)
@Font(font = "MyFont.otf", textviews = { R.id.tv1, R.id.tv2 })
public class MyActivity extends Activity {

And

@EActivity(R.layout.main)
@Fonts({
@Font(font = "MyFont.otf", textviews = { R.id.tv1, R.id.tv2 }).
@Font(font = "MyFont2.otf", textviews = { R.id.tv3, R.id.tv4 })
})
public class MyActivity extends Activity {

Delete comment Comment 4 by project member mathieu.debrito, Sep 14, 2011
That's true !

Another idea is that we could get the root layout element as a view group.
from that, we can get back all the views in this view group and apply on them the new font.

The main problem with this idea is that any new view ( I mean dynamically created ) will not have the font.

Delete comment Comment 5 by project member py.ricau, Sep 14, 2011
Yep. And there is a risk of magical chaos, ie the framework doing too much things and the dev not understanding exactly what happens under the cover.
Delete comment Comment 6 by project member mathieu.debrito, Sep 14, 2011
ya,I think the following versions are OK :

@EActivity(R.layout.main)
@Fonts({
@Font(font = "MyFont.otf", textviews = { R.id.tv1, R.id.tv2 }),
@Font(font = "MyOtherFont.otf", textviews = { R.id.tv3, R.id.tv4 })
})
public class MyActivity extends Activity {

OR
@EActivity(R.layout.main)
@Font(font = "MyFont.otf", textviews = { R.id.tv1, R.id.tv2 })
@Font(font = "MyFont2.otf", textviews = { R.id.tv3, R.id.tv4 })
public class MyActivity extends Activity {
Delete comment Comment 7 by project member py.ricau, Sep 14, 2011
No, the latter can't work. You CANNOT twice the same annotation on an element, Java does not allow this. That's why I proposed @Fonts ;-)
Delete comment Comment 8 by project member mathieu.debrito, Sep 14, 2011
Actually, instead of textviews, we could have the viewgroup option :
this would set the font for all the views in the viewgroup...

@EActivity(R.layout.main)
@Font(font = "MyFont.otf", textviews = { R.id.tv1, R.id.tv2 }, viewgroup = { R.id.vg1 })
public class MyActivity extends Activity {
Delete comment Comment 9 by project member mathieu.debrito, Sep 14, 2011
Ok, got it for @Fontss, a little bit too much I think
but if we can't skirt, it will be fine ^^
Delete comment Comment 10 by project member py.ricau, Sep 14, 2011
Well, I understand the idea, but as I said before, doing magical stuff might confuse the users :

- As far as I know, only TextViews have a setTypeface methods
- Setting ViewGroup is exactly the same as defining the font for the whole activity layout. It means iterating on all views of the viewgroup, and if a child is a viewgroup then iterating again.. and checking if it's a TextView for each child, and then setting the typeface. Problem is, if you have a complex view hierarchy, you'll end up iterating on a looooot of views. And it doesn't feel good.

AndroidAnnotations aims at writing for you the boring code. You wouldn't usually write this kind of code for an activity, you'd rather retrieve each view by id. At least, I guess so.

Maybe I'm wrong, and it has no runtime impact. This needs testing. People would hate us if AndroidAnnotations had a runtime performance impact.

Anyway, maybe we could also create an @AllFont (or something) annotation that would do what you suggested, and warn the user that this might have performance impact ?
Delete comment Comment 11 by project member mathieu.debrito, Sep 14, 2011
Well, I understand your feeling for the all views.
As I saw this, a viewGroup would be included ( with a warning for the user )

But I understand the way AndroidAnnotations should keep.
I think I could check whether the change of the setFont on a viewGroup could bring runtime problems or not.

I use a viewGroup Font modifier in my apps, and didn't receive any problem so far.
Anyway, I agree with you on the fact that AndroidAnnotations should keep its way for no runtime impact !

Delete comment Comment 12 by project member mathieu.debrito, Sep 19, 2011
Here is my font modifier for a view group :

public static Typeface setFontToAllViews(AssetManager assets, String fontName, ViewGroup aView) {
Typeface font = Typeface.createFromAsset(assets, fontName);

for (int i = 0; i < aView.getChildCount(); i++) {
View v = aView.getChildAt(i);
if (v instanceof TextView) {
((TextView) v).setTypeface(font);
} else if (v instanceof Button) {
((Button) v).setTypeface(font);
} else if (v instanceof EditText) {
((EditText) v).setTypeface(font);
} else if (v instanceof CheckBox) {
((CheckBox) v).setTypeface(font);
} else if (v instanceof ViewGroup) {
setFontToAllViews(assets, fontName,(ViewGroup) v);
}
}
return font;
}

I thought a bit about the font problem, and I think there should be only 2 ways for specify font :

@Font(fontName)
@FontRecursive(fontName)

The @FontRecursive should be only accepted for viewGroup :
it should not compile else.