Genetics for Xamarin.Android

As with all my development, I try to do as little as possible 🙂 The more code you type, the more you have to maintain and bugfix.

Now, being that I enjoy working on libraries/backends rather than on UI/frontends, I like my code to be neat and clean. Sure, the final app must be good, but I read the code.

So, I looked around and found a simple way to reduce the overall boilerplate code, as well as improve the final code. Although good code doesn’t mean a good app, bad code often results in a poor app.

A Practical Example

This library is focused around only one small things, but that one small thing is done over and over again everywhere… boilerplate! This one thing is getting hold of an Android View or Resource based on the ID.

For example, if we have an Activity with X amount of UI widgets that we need to access:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical"
  android:padding="8dp">
  <TextView
    android:id="@+id/myLabel"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:textSize="50sp" />
  <Button
    android:id="@+id/myButton"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="10dp" />
  <ListView
    android:id="@+id/myListView"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1"
    android:layout_margin="10dp" />
</LinearLayout>

Then, usually we would have X number of FindViewById method calls and X fields:

private TextView myLabel;
private Button myButton;
private ListView myListView;

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

  SetContentView(Resource.Layout.MyActivityLayout);

  myLabel = this.FindViewById<TextView>(Resource.Id.myLabel);
  myButton = this.FindViewById<Button>(Resource.Id.myButton);
  myListView = this.FindViewById<ListView>(Resource.Id.myListView);
}

This is not much code as it stands, but it is a bit monotonic if you ask me… especially if you have a good few widgets on the screen.

Now, this is where Genetics comes in. We can replace all those FindViewById methods with a single Splice method call, and annotate the fields, or properties, with the [Splice] attribute:

[Splice(Resource.Id.myLabel)]
private TextView myLabel;
[Splice(Resource.Id.myButton)]
private Button myButton;
[Splice(Resource.Id.myListView)]
private ListView myListView;

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

  SetContentView(Resource.Layout.MyActivityLayout);

  Geneticist.Splice(this);
}

And that’s all there is to it!

Resources

Not only can we avoid all the FindViewById method calls, but we can also avoid querying the Resources object for the various values. Given the resource file:

<resources> 
  <string name="titleText">Genetics</string> 
</resources>

We can automatically populate the field or property in the same way as with the views.

[Splice(Resource.String.loginError)]
private string loginErrorMessage;

protected override void OnCreate(Bundle state)
{
  // ...

  Geneticist.Splice(this);
}

Fragments

A special case exists when the target object instance that contains the widgets is not an Activity, Dialog or View. This is most commonly used when inflating views, as when using a Fragment:

public class MyFragment : Fragment
{
  [Splice(Resource.Id.textView)]
  private TextView textView;

  public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle state)
  {
    var view = inflater.Inflate(Resource.Layout.MyFragmentLayout, container, false);

    Geneticist.Splice(this, view);

    return view;
  }
}

List Views

Another popular case is when working with a ListView and the view holder pattern for the list items:

public class SimpleAdapter : BaseAdapter
{ 
    public override View GetView(int position, View convertView, ViewGroup parent)
    {
        ViewHolder holder;
        if (convertView != null)
        {
            holder = (ViewHolder)convertView.Tag;
        }
        else
        {
            convertView = inflater.Inflate(Resource.Layout.SimpleListItem, parent, false);
            holder = new ViewHolder(convertView);
            convertView.Tag = holder;
        }

        holder.word.Text = ...;
        holder.length.Text = ...;
        holder.position.Text = ...;

        return convertView;
    }

    private class ViewHolder : Java.Lang.Object
    {
        [Splice(Resource.Id.word)] 
        public TextView word;
        [Splice(Resource.Id.length)] 
        public TextView length;
        [Splice(Resource.Id.position)] 
        public TextView position;

        public ViewHolder(View view)
        {
            Geneticist.Splice(this, view);
        }
    }
}

Downloading & Using

There is lots more that can be done, or rather, not done, using Genetics. The best way to get hold of this library is either through the Xamarin Components Store or from NuGet.

This library is really tiny, and has no dependencies other than Android…

Lots more information can be found on the Getting Started guide as well as in the github.