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 source on GitHub.