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

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; 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);

      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!