UI “Cast Away”

How it works

http://code.google.com/p/android-ui-inference/

Activity and Fragment implementation is one of the most important part of an Android Project. In these components we manage Views getting their reference with code like this:

TextView mTextView = (TextView)findViewById(R.id.my_testview);
Button mButton = (Button)findViewById(R.id.my_button);
EditText mEditText = (EditTExt)findViewById(R.id.my_edittext);

and so on. I found the usage of the (cast) operation so boring so I decided to create this super simple (but not obvious for non Java expert) utility class that eliminate that key-typing overhead.

So far the library contains only two methods. In the case we already have the reference to the root View, that we call containerView, the previous lines of code become:

TextView mTextView = UI.findViewById(rootView ,R.id.my_testview);
Button mButton = UI.findViewById(rootView ,R.id.my_button);
EditText mEditText = UI.findViewById(rootView ,R.id.my_edittext);

The code for this method uses Java generics:

@SuppressWarnings("unchecked")
public static <T extends View> T findViewById(View containerView, int viewId){
    // We find the view with the given Id
    View foundView = containerView.findViewById(viewId);
    // We return the View with the given cast
    return (T) foundView;
}

The process that permits us to do that is called Type Inference and is implemented with the

<T extends View> T

declaration. In this way the type of the returned object is inferred but the type of the variable we assign the result to.

The second method is:

public static <T extends View> T findViewById(Activity act, int viewId) {
    // We get the VuewGroup of the Activity
    View containerView = act.getWindow().getDecorView();
    return findViewById(containerView, viewId);
}

This overload just get the containerView from the Activity.

Other considerations

An alternative should be the usage of Roboguice that uses annotations to get View references with code like this:

public MyActivity extends RoboActivity{

    @InjectView(R.id.my_textview) private TextView mTextView;

    @InjectView(R.id.my_button) private Button mButton;

    @InjectView(R.id.my_edittext) private EditText mEditText;

    - - -

}

This is a good solution if you want to use Roboguice framework but your Activity should extend RoboActivity that can be a problem if you use other framework that do the same (ActionBarSherlock for instance).

Both Roboguice and my code have no compile type checking. If you write this code

TextView mTextView = UI.findViewById(rootView ,R.id.my_button);

and R.id.my_button is a Button, no compile error is generated but a runtime exception instead. By the way, the same happens when you write this code:

TextView mTextView = (TextView)findViewById(R.id.my_my_button);

or this:

@InjectView(R.id.my_button) private TextView mTextView;