Monday, March 13, 2017

Kill AXML - Programmatic ListViews in Xamarin Android

Displaying list data in Android using a custom layout is traditionally accomplished by inflating an AXML file for each row of data.  However, in my article introducing EasyLayout.Droid I made the case that AXML files slow development speed and decrease cross platform re-usability.

In this article, I'll show how to build custom Android ListView's 100% programmatically, and hopefully I'll convince you that the results are cleaner, more modular, and more maintainable.



Misguided Guidance?


Displaying a list of data in Android with one of the built-in row views, such as SimpleListItem1 for a single line of text or ActivityListItem for text plus an image, is well documented and fairly straightforward.

But to display list data using a custom layout, the Xamarin documentation makes it sound like you're best off using an AXML file:

The four built-in row views are very simple. To display more complex layouts (such as a list of emails, or tweets, or contact info) a custom view is required. Custom views are generally declared as AXML files in the Resources/Layout directory and then loaded using their resource Id by a custom adapter. The view can contain any number of display classes (such as TextViews, ImageViews and other controls) with custom colors, fonts and layout.

Then later in the documentation it sounds like you're required to use an AXML file:

Another AXML layout file is required to contain the custom layout for each row that will appear in the list view.

In fact, not only are AXML files optional, but if you follow the guidance in the documentation you'll end up with code that isn't SOLID.

All Other Ground is Sinking Sand


The documentation includes the following example that illustrates the best practice of view recycling, whereby an AXML file is only inflated when absolutely necessary (i.e. view == null), and the rest of the time the view is reused and subviews are extracted and updated.

public override View GetView(int position, View convertView, ViewGroup parent)
{
    var item = items[position];
    View view = convertView;
    if (view == null) // no view to re-use, create new
        view = context.LayoutInflater.Inflate(
            Resource.Layout.CustomView, null);
    view.FindViewById<TextView>(Resource.Id.Text1)
        .Text = item.Heading;
    view.FindViewById<TextView>(Resource.Id.Text2)
        .Text = item.SubHeading;
    view.FindViewById<ImageView>(Resource.Id.Image)
        .SetImageResource(item.ImageResourceId);
    return view;

}

I have a couple of problems with this code:

  1. Where would you add an event subscription if the view required a button?
  2. Where would you initialize the font size, font family, or color of a text view if you wanted to share the code between iOS and Android?
  3. How would you handle making subtle changes to the UI based on the type of item?

The correct answer to #1 and #2 is that all one-time setup code belongs inside the if statement.  If, for example, you were to put an event subscription along-side the FindViewById statements you would be in for a nasty surprise if you tapped the button after scrolling up and down the list a few times (let alone how and when would you unsubscribe).

The correct answer to #3 is that you'd have to put an additional if block outside of the 1st if statement, and in the process, violate the Open/Closed principle of SOLID.

The example also violates the Single Responsibility principle of SOLID in that it combines setup code that occurs infrequently and view recycling code that occurs quite frequently.  

So, while the code above looks innocuous enough, it encourages you down a bad path whereby you're liable to violate several tenants of SOLID. 

A World Without XML


I'm proposing keeping the view recycling -- except, instead of inflating the view from AXML, you'll instantiate a custom view.  Then, instead of pulling sub-views out of the inflated view and updating them with FindViewById() you'll just ask the view to update itself:

public override View GetView(int position, View view, ViewGroup parent)
{
    var productRowView = view as ProductRowView ??
        new ProductRowView(_context);
    var product = _items[position];
    productRowView.Update(product);
    return productRowView;

}

GetView() is now shorter and much cleaner because it has fewer responsibilities -- mostly determining when to instantiate a new view (not how to do it) or when to update a view (and not how to do that).

We've moved all remaining logic into a dedicated class called, in this example, ProductRowView.  Infrequent, one-time setup code is now the responsibility of ProductRowView's constructor, and frequent view recycling code is the responsibility of the Update() method.

Plus, we could now easily subclass ProductRowView if we had different display needs for different types of products (it's more SOLID).

I suspect Uncle Bob Martin would be very happy with this change.

Where's the Beef?


ProductRowView now looks fairly similar to any 100% programmatic custom view.  One difference is it inherits from RelativeLayout, so you can (optionally) use EasyLayout's .ConstrainLayout() call.

public class ProductRowView : RelativeLayout
{
    private TextView _titleText;
    private TextView _dollarText;
    private TextView _amountText;

    public ProductRowView(Context context) : base(context)
    {
        SetViewProperties();
        AddViews();
        ConstrainLayout(this);
    }

    private void SetViewProperties()
    {
        var height = ViewUtils.DpToPx(Context, 40);
        var width = ViewGroup.LayoutParams.MatchParent;
        LayoutParameters = new ViewGroup.LayoutParams(width, height);
    }

    private void AddViews()
    {
        _titleText = this.Add<TextView>();
        _dollarText = AddDollarText(this);
        _amountText = this.Add<TextView>();
    }

    private static TextView AddDollarText(ViewGroup parent)
    {
        var dollarText = parent.Add<TextView>();
        dollarText.Text = "$";
        dollarText.TextSize = 8;
        return dollarText;
    }

    private void ConstrainLayout(RelativeLayout relativeLayout)
    {
        relativeLayout.ConstrainLayout(() =>
            _titleText.Left == this.Left + 20
            && _titleText.Top == this.Top + 10
            && _titleText.Bottom == this.Bottom - 20

            && _amountText.Right == this.Right - 20
            && _amountText.Top == this.Top + 10
            && _amountText.Bottom == this.Bottom - 20

            && _dollarText.Right == _amountText.Left
            && _dollarText.Top == _amountText.Top
        );
    }

    public void Update(Product product)
    {
        _titleText.Text = product.Title;
        _amountText.Text = product.Amount.ToString("0.00");
    }

}



The result looks like this:


To help with long-term maintainability I've organized this into three high-level methods:

  • SetViewProperties() 
  • AddViews(); and
  • ConstrainLayout()


Also, I've tried to keep one line per view instantiation in AddViews().  For example, AddDollarText() is a method that is solely responsible for instantiating a single view.

This small method approach may appear at first glance to be more work than worth.  However, I would highly encourage this approach because I've found that if you aren't careful when creating a new 100% code-based view, over time it will become long and gnarly with methods spanning hundreds of lines.

Does this approach actually seem cleaner?  Consider the following questions:

  • If you wanted to extract the color of the amount label into a cross platform variable, where would you initialize that color?  
  • How would you handle buttons with event subscriptions?
  • What if you needed to display an OutOfStockProduct using a completely different style of dollar and amount labels (subtype the view maybe)?

I hope you'll agree the answers are generally more obvious, that the design has more flexibility, and that this sets future maintainers up for success.

Summary


Hopefully this helps with how to build list-based UI's on Android without the AXML.  You can see the full example on github in ViewProductsActivity.cs.

I hope you'll consider taking the technique for a spin and deciding for yourself if your next Android project would be better without the AXML.

Sunday, January 8, 2017

Introducing EasyLayout.Droid For Simpler Xamarin Android Layouts

If you've done much Xamarin iOS work you're probably run into Frank Krueger's awesome framework, EasyLayout, that makes manually coded auto layout's considerably easier to read and maintain.

If you've ever wanted the same type of functionality for Xamarin Android either for consistency or ease of cross platform code sharing, now you can with EasyLayout.Droid.


What Is EasyLayout?


The original EasyLayout takes Xamarin iOS code like this:

_passwordField.AddConstraint(NSLayoutConstraint.Create(
    _passwordField, NSLayoutAttribute.Top, NSLayoutRelation.Equal,
    _usernameTextField, NSLayoutAttribute.Bottom, 1f, 20f));
_passwordField.AddConstraint(NSLayoutConstraint.Create(
    _passwordField, NSLayoutAttribute.CenterX, NSLayoutRelation.Equal,
    View, NSLayoutAttribute.CenterX, 1f, 0f));

And turns it into this:

View.ConstrainLayout(() =>
    _passwordField.Frame.Top == _usernameTextField.Frame.Bottom + 20 &&
    _passwordField.Frame.GetCenterX() == View.Frame.GetCenterX()
    );

If you're on a team, or storyboards just aren't your thing it's a lifesaver!


What's Wrong with Android .axml?


Android's axml files are ok, but on large projects they take a long time to generate, and they make it hard to share layout information cross platform.  But if you try to code Android by hand, you quickly discover the same type of verbosity that Xamarin iOS had.  Enter EasyLayout.Droid.

Example 1 - Parent Align


If you want to align an image to the edges of the frame you used to do this:

var layoutParams = new RelativeLayout.LayoutParams(
    ViewGroup.LayoutParams.MatchParent,
    ViewGroup.LayoutParams.MatchParent);
layoutParams.AddRule(LayoutRules.AlignParentTop);
layoutParams.AddRule(LayoutRules.AlignParentBottom);
layoutParams.AddRule(LayoutRules.AlignParentRight);
layoutParams.AddRule(LayoutRules.AlignParentLeft);
_image.LayoutParams = layoutParams;

Now you can do this:

relativeLayout.ConstrainLayout(() =>
    _image.Top == relativeLayout.Top
    && _image.Right == relativeLayout.Right
    && _image.Left == relativeLayout.Left
    && _image.Bottom == relativeLayout.Bottom
    );

There's no need to set LayoutParams at all.  If they don't exist EasyLayout.Droid will add them, if they do EasyLayout.Droid will append to them.  And if you don't add them it will take care of choosing LayoutParams.MatchParent or WrapContent.

Example 2 - Relative Alignment and Constants


If you wanted to align an image 20 dp under another image and center align it to the parent you used to do this:

var layoutParams = new RelativeLayout.LayoutParams(
    ViewGroup.LayoutParams.WrapContent,
    ViewGroup.LayoutParams.WrapContent);
layoutParams.AddRule(LayoutRules.CenterHorizontal);
layoutParams.AddRule(LayoutRules.AlignBottom, image1.Id);
layoutParams.TopMargin = DpToPx(20);
_image2.LayoutParams = layoutParams;


There's a couple of gotchas.  

  1. If you set the TopMargin to 20, then Android assumes you mean pixels not device independent pixels.  To fix that you need to remember to call a function like DpToPx().  
  2. Your relative view (image1) needs to have an Id.  If you forget to set it there's no error, it just does strange layout things.

EasyLayout.Droid replaces the code above with:

relativeLayout.ConstrainLayout(() =>
    _image2.Top == _image1.Bottom + 20
    && _image2.GetCenterX() == relativeLayout.GetCenterX()
    );


That's less code, and it's easier to read, plus there's some other small benefits: 

  1. If you forget to add an Id to _image1, EasyLayout.Droid will throw a helpful runtime error.  
  2. EasyLayout.Droid always assumes every number is in Dp, so it automatically converts all literals for you.

Incidentally, GetCenterX() is one of a couple of new extension methods along with GetCenterY() and GetCenter().

Example 3 - Constants


Constants weren't difficult to work with previously, but for completeness they used to work like this:

var layoutParams = new RelativeLayout.LayoutParams(
    DpToPx(50),
    DpToPx(ViewModel.SomeHeight);
_image2.LayoutParams = layoutParams;


With EasyLayout.Droid you can do this:

relativeLayout.ConstrainLayout(() =>
    _image.Width == 50
    && _image.Height == ViewModel.SomeHeight.ToConst()
    );


As mentioned previously 50 will be assumed to be in dp and will be auto-converted to pixels.  Also arbitrary properties such as SomeHeight will need the .ToConst() extension method applied in order to tell EasyLayout.Droid that they should be treated as constants.

Limitations


Android relative layouts are far from a replacement for iOS's auto layout.  To that end you cannot do the following operations that EasyLayout could:

  • Constraining Heights or Widths to be equal to the Heights or Widths of other elements
  • Using >= or <= signs to indicate GreaterThanOrEqual or LessThanOrEqual type constraints
  • Multiplication of elements (e.g. _image2.Width == _image1.Width * .25f)

Installation


If you want to add this to your project you can either install via NuGet (safer):

Install-Package EasyLayout.Droid

or if you think it's perfect as-is (you don't want updates) you can copy EasyLayoutDroid.cs into your source.  Next using EasyLayout.Droid and you're good to go.

Conclusion


Hope this helps make someone's Xamarin Android day a little better.  The code is MIT licensed.  If you have any questions please contact me on twitter.


Benefits Of Git Rebase

In the first article in this series (Git: Rebase vs Merge) I covered the tactics of rebasing.  I discussed what merge commits are, and how to avoid them with rebasing.  In this post I'll cover the benefits of rebasing, including how its use speeds up finding hard to track down bugs via git blame and git bisect.

Is Rebase Really Worth It?


I worked on one large project that discouraged rebasing a while ago.  In short, this was the result:


The repository was insanely complicated to look at with gitk (or SourceTree in this case).  Trying to understand what was going on across the program was virtually impossible.
The recalcitrant developer (hey, someone forced me to learn these stupid SAT words, might as well inflict them on others), at this point, might simply respond: "So what?  As long as my stuff works."  And perhaps it's true, just like the old joke:
Patient: Doctor, it hurts whenever I do this.
Doctor: Don't do that.

Merge Commits: Ugly, Painful, or Both?


But even if someone never looked at the repo with gitk, or a fancy git GUI, in the project above roughly 50% of all commits were merge commits!
That meant 50% of the history of the project was useless noise distributed like a shotgun blast into the true history.  Furthermore, some of those merge commits hid sneaky little bugs introduced when merge conflicts were poorly resolved.  
Granted, 50 percent may be high for most projects.  The actual number depends on how many developers, how often they commit, and how often they merge.  For example if developers pull from develop daily, a given project will get one merge commit per developer per day.  If developers commit 3 times per day (seems kind of average from my observations), then 25% of commits will be merge commits.
The recalcitrant developer (stupid SAT) might again at this point respond: "25% of commits are merges, so what?  Just don't look at the commit history!  And if forced to, just ignore the merge commits!"
However, there are two specific cases where a messy history may yet affect a fast and loose, merge happy team.

Don't Blame Git!


Have you ever looked at a file and wondered who wrote a particular line of crap?  In my case 99% of the time that person is me.  But never mind that.  The tool for this job is clearly git blame.
The command git blame (or git annotate for the more more politically correct) will annotate every line of a particular file with its last commit.  Apart from finding who wrote something, it's also an essential tool for discovering why something was done the way it was when spelunking through larger, or especially older, codebases.
However, merge commits obfuscate git blame.  If an associated commit message is simply "merged develop into feature.awesome" and the developer is no longer around to ask, then we have to go through additional effort to track down history.

For instance, in the example above line 3 "c" was actually created by commit C (b49c7b1), but git blame incorrectly shows the merge commit (d9400d4) as the author.

Git Bisect For The Win!

The second scenario in which merge commits complicate history is in tracking a bug that someone introduced, typically, within the last few days.  One could manually checkout every commit between the good commit and the bad commit, or simply use git bisect.  
Git bisect is wonderful for automating the process of finding a commit.  It allows you to specify the last known good commit, and the last known bad commit, and then it performs a binary search of all the commits in between to discover the bad commit as quickly as possible.
Regardless of whether you search manually, or use git bisect, life gets hard as soon as you try to juggle many branches with lots of merge commits.  The automated approach makes navigating many merged branches easier, but either way if you have a fully merge-based project, you are now required to take an additional 25-50% steps.   If each step takes time to build or deploy, these extra steps can quickly add up (trust me, I've had to do this a lot).
For instance consider the following project with three feature branches, seven real commits, and five merge commits.
gitbisect2.png
Now pretend that you've come in on Monday morning to discover that after committing D on Friday, some developers over the weekend committed E and F, and suddenly there's a hard to track down bug.  Git bisect will solve it for you like this: 
Lee@lee-xps MINGW64 /c/Temp/deletemegit (feature.awesomeness)
$ git bisect start
Lee@lee-xps MINGW64 /c/Temp/deletemegit (feature.awesomeness|BISECTING)
$ git bisect good 880e84a
Lee@lee-xps MINGW64 /c/Temp/deletemegit (feature.awesomeness|BISECTING)
$ git bisect bad be49c0d
Bisecting: 5 revisions left to test after this (roughly 3 steps)
[d9400d4c62807046f8ea235170e681b3e8952200] Merge branch 'develop' into feature.awesomeness
Lee@lee-xps MINGW64 /c/Temp/deletemegit ((d9400d4...)|BISECTING)
$ git bisect good
Bisecting: 2 revisions left to test after this (roughly 2 steps)
[96cf2877d16183cccce1f822d72626d331b582ef] Merge branch 'develop' into feature.awesomeness
Lee@lee-xps MINGW64 /c/Temp/deletemegit ((96cf287...)|BISECTING)
$ git bisect bad
Bisecting: 0 revisions left to test after this (roughly 1 step)
[190dadb1046822fc169193e86484a19e3543b783] Merge branch 'feature.2' into develop
Lee@lee-xps MINGW64 /c/Temp/deletemegit ((190dadb...)|BISECTING)
$ git bisect bad
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[ef4a5532ace6075aead0850f088322f98e7afbf1] E
Lee@lee-xps MINGW64 /c/Temp/deletemegit ((ef4a553...)|BISECTING)
$ git bisect bad
ef4a5532ace6075aead0850f088322f98e7afbf1 is the first bad commit
commit ef4a5532ace6075aead0850f088322f98e7afbf1
Author: Lee Richardson
Date:   Mon Sep 5 21:56:07 2016 -0400
    E
:000000 100644 0000000000000000000000000000000000000000 d8263ee9860594d2806b0dfd1bfd17528b0ba2a4 A      2.txt
Lee@lee-xps MINGW64 /c/Temp/deletemegit ((ef4a553...)|BISECTING)
$ git bisect reset
Previous HEAD position was ef4a553... E
Switched to branch 'feature.awesomeness'
Your branch is based on 'origin/feature.awesomeness', but the upstream is gone.
  (use "git branch --unset-upstream" to fixup)
Lee@lee-xps MINGW64 /c/Temp/deletemegit (feature.awesomeness)

Beautiful!  Commit E was the culprit.  The only problem: three of the four steps wouldn't have been needed if the team had been rebasing.

Summary


If your project is still small, and you haven't had to use git blame or git bisect yet, you may not find these arguments compelling.  However, if this is the case I suspect you may also  not find much value in unit testing either.  Unit testing and rebasing both require extra up-front work in order to build quality into your work and set your future self up for success.
More worried about immediate deadlines than your future self?  Consider the developer who will replace you when you leave.  Not worried about her?  Consider your customer at some future point in time when they're attempting to spelunk through your code.  Unless this is a throwaway project, the chances are good that a little extra effort learning a new technique today could save yourself, other developers, and possible a future employer considerable time and energy.  Not a bad investment.

Tuesday, September 13, 2016

Git: Rebase VS Merge

Distributed version control (git): Check.
Feature branches: Check.
Rebasing: Ugh, serioiusly.

On multiple projects I've attempted to introduce the concept of rebasing commits.  It rarely goes well (at first).  The initial reaction tends to be reluctance and confusion.  Reluctance, because it adds complexity and appears to give nothing back.  Confusion, because doing it wrong, and intertwangling feature branches in particular, turns out to be way too easy.
This is the first post in a series that covers how and why to make a clean commit history.  In this post I'll cover the history of merging, the difference between merging and rebasing, and the golden rule of rebasing.

In subsequent posts I'll cover the benefits of rebasing, cover common pitfalls, and provide several concrete workflow tips you can implement today to leave your project with a simple, clean, and linear source history with the ability to easily track down newly introduced issues.

The Good Old Days

Remember Source Safe and CVS?  Those were the good old days.  You'd simply check out files you were working on, and no one else could touch them.  No merge conflicts, ever.  It just worked, right?
That was great - right up until someone went on vaction and left a ton of files checked out, leaving the entire team unable to work.  Oh yea, not so great.
But remember subversion?  Those were the good old days.   Optimistic concurrency to the rescue.  Multiple people could work on the same file and 90% of the time it just worked.  And branching was super easy.
Subversion was great.  Right up until you had to merge branches.  Then it was time switch projects, and ASAP.  Merging was always a complete disaster.  Maybe subversion wasn't so great after all.
Git has revolutionized source control, first and foremost by making branching and merging easy.  Distributed version control systems (DVCS) like git allow us to work on features independently, and defer integrating with the rest of the team until the time is right.  Since we can commit locally, we can use small, frequent commits, thus allowing us to break complex features into smallier pieces and allowing our commit history to tell a story.
However, with git, while merging may be much easier than it once was - we still have to choose between two approaches.

To Merge or Not, That is the Question

The easiest option, and what most do by default is git merge.  Say you've started feature.awesomeness and committed "B" and "D" to it.  Except meanwhile back on develop someone committed "C".  
pre-merge.png
No big deal, you merge develop into feature.awesomeness with git merge develop and give it a nice descriptive commit message like "Merge branch 'develop' into feature.awesomeness".
merge-commit1.png
That last commit, a merge commit, is a necessary side-effect, and a teltale sign, of merging.  You'll end up with one of these sketchy looking commits each time you want to integrate with (pull from) another branch.
When the feature is done you can merge it back to develop with git checkout develop and git merge feature.awesomeness. And thanks to the magic of fast-forward you won't have a second commit in develop.
post-merge.png
While there the extra merge commit may be aesthetically unpleasing, and the lines branching and merging in the diagram a little noisy; this merge approach is easy, it's clear what happened, the commits maintain their chronology (A, B, C, D).

Marty McFly: "Yeah, well, history is gonna change"

The alternative is rebasing.  With this technique, you replay all of your local commits on top of another branch (usually develop or master).  This essentially deletes your original commits and does them again in the same order.  The other branch becomes the new "base".  So In the scenario above you'd do git rebase develop and end up with:
rebase1.png
Pretty.  Clean.  You can pull from develop as many times as you want, and you'll never get extra commits.
If you're done, you switch back to develop and with a quick git merge and a free fast-forward you get:
rebase2.png
Notice that the commits are no longer chronological (A, C, B, D rather than A, B, C, D) and that B and D both have the same commit time, 21:07.  Duplicate checkin times are the teltale sign of rebasing.    
Sounds just as easy as merging, looks clean, but rebasing can be dangerous.

Golden Rule of Rebasing

Since rebasing changes history, the most important rule is never to rebase after you're pushed to a remote branch.  Or, more subtly, never rebase if someone might be using using your remote branch (e.g. pure backups are ok).  The reasoning: if you've changed history, you can only push again with a git push -force, and forcing the push will overwrite remote history, making a mess for anyone needing to reconcile your old commits with your new ones.
An important corollary to the golden rule of rebasing is that if you push your local feature branch to origin but don't merge it to origin/develop, you are (usually) giving up the opportunity to rebase ever again on that branch.  In other words, if you want the benefits of rebasing (generally) refrain from pushing until you're ready to merge it back.
For example imagine you have a local unpushed branch like this:
goldenruleB1.png
Now you'd like to share your commit B with the world.  However, you aren't quite ready to merge it back to develop yet, and so you push without merging:
goldenruleB2.png
This may look fine, but you've just lost your ability to rebase.  This can be verified by examining the case where someone subsequently makes a change back on develop (C) while you have unpushed local changes (D):
goldenruleB3.png
Now if you rebase, you'll rewrite B, and that will result in a two commits called B, one locally and one on the server in origin/feature.awesomeness:
goldenruleB4.png
In this case git push -force can sometimes help, but it can also open a whole other can of worms.  The best option is to maintain the ability to rebase isby not pushing until it's absolutely necessary.

Conclusion

Hopefully you've learned a little about the history of merging, what merge commits are, how rebasing can elimiate merge commits, and a little of how to avoid rebasing trouble.  
But what if you're working on a large feature and you want to share it with another branch, but not merge back yet?  Are the benefits of rebasing really worth it?  This post has been more about tactics.  I'll cover strategy in subsequent posts.