How to Auto Wire Up Android Activity Views in Xamarin.Android

·

3 min read

One thing that’s occasionally bothered me was how verbose it is in Android to wire up views from a layout, in an activity. I finally was fed up enough to write a simple little helper that does this based on reflection. Consider the following verbosity monstrosity:

[Activity (Label = "RandomDataActivity")]
public class RandomDataActivity : Activity
{
    Button button1;
    Button button2;
    Button button3;
    Button button4;
    TextView textView1;
    TextView textView2;
    TextView textView3;
    TextView textView4;
    TextView textView5;
    TextView textView6;
    TextView textView7;
    TextView textView8;
    TextView textView9;
    ImageView imageView1;
    ImageView imageView2;
    ImageView imageView3;
    ImageView imageView4;
    ImageView imageView5;
    ImageView imageView6;

    protected override void OnCreate (Bundle bundle)
    {
        base.OnCreate (bundle);

        SetContentView (Resource.Layout.RandomDataLayout);

        button1 = FindViewById<Button>(Resource.Id.button1);
        button2 = FindViewById<Button>(Resource.Id.button2);
        button3 = FindViewById<Button>(Resource.Id.button3);
        button4 = FindViewById<Button>(Resource.Id.button4);
        textView1 = FindViewById<TextView>(Resource.Id.textView1);
        textView2 = FindViewById<TextView>(Resource.Id.textView2);
        textView3 = FindViewById<TextView>(Resource.Id.textView3);
        textView4 = FindViewById<TextView>(Resource.Id.textView4);
        textView5 = FindViewById<TextView>(Resource.Id.textView5);
        textView6 = FindViewById<TextView>(Resource.Id.textView6);
        textView7 = FindViewById<TextView>(Resource.Id.textView7);
        textView8 = FindViewById<TextView>(Resource.Id.textView8);
        textView9 = FindViewById<TextView>(Resource.Id.textView9);
        imageView1 = FindViewById<ImageView>(Resource.Id.imageView1); 
        imageView2 = FindViewById<ImageView>(Resource.Id.imageView2);
        imageView3 = FindViewById<ImageView>(Resource.Id.imageView3);
        imageView4 = FindViewById<ImageView>(Resource.Id.imageView4);
        imageView5 = FindViewById<ImageView>(Resource.Id.imageView5);
        imageView6 = FindViewById<ImageView>(Resource.Id.imageView6);
    }
}

Now that’s a lot of code to do something really simple. Plumbing code that’s so simple I’d rather not, and shouldn’t have to waste my time on it! How about this instead:

[Activity (Label = "RandomDataActivity")]
public class RandomDataActivity : Activity
{
    Button button1, button2, button3, button4;
    TextView textView1, textView2, textView3, textView4,
             textView5, textView6, textView7 textView8,
             textView9;
    ImageView imageView1, imageView2, imageView3, imageView4,
              imageView5, imageView6;

    protected override void OnCreate (Bundle bundle)
    {
        base.OnCreate (bundle);
        SetContentView (Resource.Layout.RandomDataLayout);
        this.WireUpViews ();
    }
}

Here’s the extension method I created to do this:

public static class ActivityExtensions
{
    public static void WireUpViews(this Activity activity)
    {
        //Get all the View fields from the activity
        var members = from m in activity.GetType ().GetFields (BindingFlags.NonPublic | BindingFlags.Instance)
                      where m.FieldType.IsSubclassOf (typeof(View)) select m; 

        if (!members.Any ())
            return;

        members.ToList ().ForEach (m => { 
            try {
                //Find the android identifier with the same name
                var id = activity.Resources.GetIdentifier(m.Name, "id", activity.PackageName);
                //Set the activity field's value to the view with that identifier 
                m.SetValue (activity, activity.FindViewById (id)); 
            } catch (Exception ex) {
                throw new MissingFieldException ("Failed to wire up the field " + m.Name + " to a View in your layout with a corresponding identifier", ex); 
            }
        });
    }
}

Ok, ok, I know it’s not perfect, but it’s a bit of a time saver, and keeps my code looking a bit neater. In a perfect world I should just be able to use an android identifier as if it were already a declared as a code field. There’s also room for improvement in my extension method, for example, what if you declare a field that’s a View but not in the layout, but you did this intentionally? Well, I don’t want to do ALL your work for you 😉

Just wanted to share this little bit with you. Hopefully it sparks some ideas for something greater in your day! Happy Coding!