InfernoRed Blog

Introducing EasyLayout.Droid

Posted by Lee Richardson on Jan 11, 2017 3:33:06 PM

If you've done much Xamarin iOS work you've probably run into Frank Krueger's awesome framework, EasyLayout, that makes manually coded auto layouts 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 a new framework I just published called EasyLayout.Droid.

What Is EasyLayout?

The original EasyLayout takes Xamarin iOS code like this:

_passwordField.AddConstraint(NSLayoutConstraint.Create(
    _passwordField, NSLayoutAttribute.Top, NSLayoutRelation.Equal,
    _usernameField, 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 == _usernameField.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.

SampleProject.png

EasyLayout.Droid replaces the code above with:

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

That's less code and 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 <= 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 import 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.

 

Lee_-71-square-150px.jpgLee_-71-square-150px.jpgLee Richardson is a senior software developer at InfernoRed Technologies with a passion for .Net development, open source software, continuous integration, building cross platform mobile apps, and hardware hacking. In his spare time he runs Siren of Shame.  He has been consulting in the Washington DC area for over 17 years.  Keep up with him @lprichar.

 

Topics: Xamarin

About Us

InfernoRed was founded in 2012 with the mission to be a software development company for and by developers. The InfernoRed team has a passion for building cutting-edge software. Our culture is a reflection of the incredible talent that we have assembled since starting in 2012.

Subscribe to Email Updates

Recent Posts