Android: better way to apply custom fonts

Posted by Unknown Senin, 27 Februari 2012 0 komentar

In one application I had to apply customer's brand font for all controls of the user interface.
Sounds like a pretty common task, right? Yeah, I was thinking the same.
But then I was surprised that Android doesn't provide simply and elegant way to do this.

So, in this article I will show you what Android provides us by default. Then I will share with you my solution, which allows you to apply custom fonts in a more simple and elegant way.

Situation:
you have a custom font that should be applied for entire application screen.

Solution:
1) Android's default #1:
You can refer by id view controls, and apply typeface for each of them. In case of one view it looks not so scary:
Typeface customFont = Typeface.createFromAsset(this.getAssets(), "fonts/YourCustomFont.ttf");
TextView view = (TextView) findViewById(R.id.activity_main_header);
view.setTypeface(customFont);
But in case of many TextView, Buttons, etc. view at your screen you will not love this approach, I can assure you! :D

2) Android's default #2:
You can create subclass for each TextView, Button, etc. and apply custom font in the constructor:
public class BrandTextView extends TextView {

public BrandTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public BrandTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public BrandTextView(Context context) {
super(context);
}
public void setTypeface(Typeface tf, int style) {
if (style == Typeface.BOLD) {
super.setTypeface(Typeface.createFromAsset(getContext().getAssets(), "fonts/YourCustomFont_Bold.ttf"));
} else {
super.setTypeface(Typeface.createFromAsset(getContext().getAssets(), "fonts/YourCustomFont.ttf"));
}
}
}

Then just use that custom views instead of standard ones (i.e. BrandTextView instead of TextView).
<com.your.package.BrandTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="View with custom font"/>
<com.your.package.BrandTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
android:text="View with custom font and bold typeface"/>
Also, you can even add your own attribute to set required font directly via XML. To make you will need to define your own declare-styleable attributes and parse attributes in the constructor.
In order not to describe the basic things, I just point for a good article by Kevin Dion with an
example of custom attributes implementation:
http://kevindion.com/2011/01/custom-xml-attributes-for-android-widgets/

In general solution #2 not so bad and has it's own advantages (for example, to switch between regular, bold, etc. fonts based on specified "typeface" attribute value). But in my opinion it's still too heavy and requires a lot of boilerplate-code for such a simple task as applying custom font.

3) My solution:
the ideal solution would be to define custom theme and apply it to entire application or Activity.
But unfortunately Android's android:typeface attribute can only use inbuilt fonts, but not the custom fonts (e.g. from the assets). That is why we can't get away from the loading and applying fonts at runtime in Java code.
So I decided to create a helper class to make it as simple, as possible.
The usage of it looks like:
FontHelper.applyFont(context, findViewById(R.id.activity_root), "fonts/YourCustomFont.ttf");
And this one string will apply custom font for all TextView based controls (TextView, Button, RadioButton, ToggleButton, etc.) at your screen regardless of their layout hierarchy! ;-)
Standard (left) and Custom (right) fonts usage.

How this was done? Fairly simple:
public static void applyFont(final Context context, final View root, final String fontName) {
try {
if (root instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup) root;
for (int i = 0; i < viewGroup.getChildCount(); i++)
applyFont(context, viewGroup.getChildAt(i), fontName);
} else if (root instanceof TextView)
((TextView) root).setTypeface(Typeface.createFromAsset(context.getAssets(), fontName));
} catch (Exception e) {
Log.e(TAG, String.format("Error occured when trying to apply %s font for %s view", fontName, root));
e.printStackTrace();
}
}
As you can see there is nothing more then looking for TextView based views at all levels of layout hierarchy.
You can download sample project with usage of FontHelper here.



Baca Selengkapnya ....

Android: possible issues & solutions with configuration changes, part 1

Posted by Unknown Rabu, 15 Februari 2012 0 komentar

In this article I want to review the situation that occurs in the development of almost every Android application, the problems that may arise, as well as solutions to handle them.

Situation:
your application needs to receive some data via network from the back-end.

For example, user goes to some screen, appropriate Activity object initiates a network call to request XML or JSON data to parse it and show in the UI. So, typical implementation scheme could be:
in a separate thread (to prevent UI blocking) make HTTP request, receive response and parse it. When thread has finished the task, you need to notify someone (usually the caller, or Activity) that data was received and ready to be shown.

Usually, you can meet implementation with AsyncTask (as a worker thread) that has a reference to a caller Activity or some callback object. And this will work without issues until the device configuration changes.

Problem:
when the configuration of the device changes, then you may find unexpected behavior and even crashes (for example, if you will try to dismiss progress dialog at onPostExecute method of the AsyncTask object). Why? Unless you specify otherwise, a configuration change (screen orientation, input devices, language, etc) will cause your current Activity to be destroyed, going through the normal activity lifecycle process. This is done because any application resource, including layout files, can change based on any configuration value.

So, if your current Activity was destroyed and re-created, while the worker thread was active, then at worker thread completion moment you will have non actual references to a caller Activity, callback, etc.
The key questions is: how to guarantee, that worker thread will have valid return point reference at the moment of completion?


Solutions:
While, described problem is very common, Android doesn't suggest one proven and proper way to handle it. So, I will show several possible solutions with their pros and cons.

1) android:configChanges="orientation"
This is the simplest and... definitely not the best solution.
All you need to do is to add to the activity declaration in the manifest attribute "android:configChanges" and specify configuration changes types you want to handle by yourself. For example:
     <activity
            android:name=".activity.HomeActivity"
            android:label="@string/home_name"
            android:configChanges="orientation"/>
But, handling the configuration change by yourself can make it much more difficult to use alternative resources, because the system does not apply them for you automatically. So, according to Android team recommendation, this technique should be considered a last resort when you must avoid restarts due to a configuration change and it is not recommended for most applications.

2) retaining an object during a configuration change
Activity has well defined lifecycle and we can retain some state object between runtime configuration change. So, you may override onRetainNonConfigurationInstance method to return the object, that keeps references to running worker instances (e.g. AsynTask objects). Then when your activity is created again, call getLastNonConfigurationInstance to recover your state object and update AsyncTask's references to caller, callback, etc.
You can read more about it here:
http://developer.android.com/guide/topics/resources/runtime-changes.html
Also, onRetainNonConfigurationInstance/getLastNonConfigurationInstance pair is useful to retain and restore partially downloaded data (instead of re-fetch the data from the scratch).
While this approach works pretty well, I don't like that I had to define a class for state object that keeps references to running AsyncTask instances and all other stuff.

3) Ordered Broadcast and IntentService
This approach is well described by Murk Murphy (author of the great "The Busy Coder’s Guide to ..." books series). So give a word to him:
http://commonsware.com/blog/2010/08/11/activity-notification-ordered-broadcast.html

4) Event Bus
This approach was designed by me, while I was thinking about easy to use solution, to be notified when some background task becomes completed. The powerful and easy to use event dispatching model of Adobe Flex was an inspiration. I will describe this approach in details in the second part of this article. It will come soon, so be in touch! ;-)



Baca Selengkapnya ....
Trik SEO Terbaru support Online Shop Baju Wanita - Original design by Bamz | Copyright of android illegal.