Jekyll2022-03-05T22:55:26+00:00https://lucasr.org/feed.xmlLucas RochaLucas Rocha's personal website.Lucas Rochalucasr@lucasr.orgNew tablet UI for Firefox on Android2014-12-03T21:45:22+00:002014-12-03T21:45:22+00:00https://lucasr.org/2014/12/03/new-tablet-ui-for-firefox-on-android<p>The new tablet UI for Firefox on Android is now available on
<a href="http://nightly.mozilla.org/">Nightly</a> and, soon,
<a href="http://aurora.mozilla.org/">Aurora</a>! Here’s a quick overview of the design
goals, development process, and implementation.</p>
<h3 id="design--goals">Design & Goals</h3>
<p>Our main goal with the new tablet UI was to simplify the interaction with
tabs—read Yuan Wang’s <a href="https://blog.mozilla.org/ux/2014/10/re-imagine-firefox-on-tablet/">blog
post</a> for
more context on the design process.</p>
<p>In 36, we focused on getting a solid foundation in place with the core UI
changes. It features a brand new tab strip that allows you to create, remove
and switch tabs with a single tap, just like on Firefox on desktop.</p>
<p>The toolbar got revamped with a cleaner layout and simpler state changes.</p>
<picture class="figure">
<img class="figure-img img-fluid " src="/wp-content/uploads/2014/12/Screenshot_2014-11-29-20-53-16.png" title="The new toolbar." />
<figcaption class="figure-caption">The new toolbar.</figcaption>
</picture>
<p>Furthermore, the fullscreen tab panel—accessible from the toolbar—gives you a
nice visual overview of your tabs and sets the stage for more advanced features
around tab management in future releases.</p>
<picture class="figure">
<img class="figure-img img-fluid " src="/wp-content/uploads/2014/12/Screenshot_2014-11-29-20-51-55.png" title="Overview of your tabs." />
<figcaption class="figure-caption">Overview of your tabs.</figcaption>
</picture>
<h3 id="development-process">Development process</h3>
<p>At Mozilla, we traditionally work on big features in a separate branch to avoid
disruptions in our 6-week development cycles. But that means we don’t get
feedback until the feature lands in
<a href="https://hg.mozilla.org/mozilla-central/">mozilla-central</a>.</p>
<p>We took a slightly different approach in this project. It was a bit like
replacing parts of an airplane while it’s flying.</p>
<p>We first worked on the necessary changes to allow the app to have parallel UI
implementations in a separate branch. We then merged the new code to
mozilla-central and did most of the UI development there.</p>
<p>This approach enabled us to get early feedback in
<a href="http://nightly.mozilla.org/">Nightly</a> before the UI was considered
feature-complete.</p>
<h3 id="implementation">Implementation</h3>
<p>In order to develop the new UI directly in mozilla-central, we had to come up
with a way to run either the old or the new tablet UIs in the same build.</p>
<p>We broke up our UI code behind interfaces with multiple concrete
implementations for each target UI, used view factories to dynamically
instantiate parts of the UI, prefixed overlapping resources, and more.</p>
<p>The new tab strip uses the latest stable release of
<a href="https://github.com/lucasr/twoway-view/">TwoWayView</a> which got a bunch of
important bug fixes and couple of new features such as smooth scroll to
position.</p>
<p>Besides improving Firefox’s UX on Android tablets, the new UI lays the
groundwork for some cool new features. This is not a final release yet and
we’ll be landing bug fixes until 36 is out next year. But you can try it now in
our <a href="http://nightly.mozilla.org/">Nightly</a> builds. Let us know what you think!</p>Lucas Rochalucasr@lucasr.orgThe new tablet UI for Firefox on Android is now available on Nightly and, soon, Aurora! Here’s a quick overview of the design goals, development process, and implementation.Joining Facebook2014-11-27T11:24:04+00:002014-11-27T11:24:04+00:00https://lucasr.org/2014/11/27/joining-facebook<p>I am really excited to announce that I’m joining Facebook in January! I’ll be
bootstrapping Android UI efforts—frameworks and user-facing stuff—in the London
office. There are still a lot of details to sort out but that’s the general
plan.</p>
<p>Why Facebook? They have an amazing hacker-driven culture, they’re
<a href="https://www.youtube.com/watch?v=fzL6Zoy_ndk">striving</a> to do open source the
right way, there’s a lot of space for experimentation, and they have <em>massive</em>
reach in the Android space.</p>
<p>I’m really looking forward to learning a lot at Facebook. And there is so much
to be done! I can’t wait to start :-)</p>Lucas Rochalucasr@lucasr.orgI am really excited to announce that I’m joining Facebook in January! I’ll be bootstrapping Android UI efforts—frameworks and user-facing stuff—in the London office. There are still a lot of details to sort out but that’s the general plan.Leaving Mozilla2014-11-26T16:57:50+00:002014-11-26T16:57:50+00:00https://lucasr.org/2014/11/26/leaving-mozilla<p>I <a href="http://lucasr.org/2011/06/14/joining-mozilla/">joined Mozilla</a> 3 years, 4
months, and 6 days ago. Time flies!</p>
<p>I was very lucky to join the company a few months before the Firefox Mobile
team decided to rewrite the product from scratch to make it more competitive on
Android. And we <em>made it</em>: Firefox for Android is now one of the <a href="https://play.google.com/store/apps/details?id=org.mozilla.firefox">highest
rated</a>
mobile browsers on Android!</p>
<p>This has been the best team I’ve ever worked with. The talent, energy, and
trust within the Firefox for Android group are simply amazing.</p>
<p>I’ve thoroughly enjoyed my time here but an exciting opportunity outside
Mozilla came up and decided to take it.</p>
<p>What’s next? That’s a topic for another post ;-)</p>Lucas Rochalucasr@lucasr.orgI joined Mozilla 3 years, 4 months, and 6 days ago. Time flies!Probing with Gradle2014-10-07T23:12:03+00:002014-10-07T23:12:03+00:00https://lucasr.org/2014/10/07/probing-with-gradle<p>Up until now, <a href="https://github.com/lucasr/probe/">Probe</a> relied on dynamic view
proxies generated at runtime to intercept View calls. Although very convenient,
this approach greatly affects the time to inflate your layouts—which
limits the number of use cases for the library, especially in more
complex apps.</p>
<p>This is all changing now with Probe’s brand new Gradle plugin which seamlessly
generates build-time proxies for your app. This means virtually no overhead at
runtime!</p>
<p>Using Probe’s Gradle plugin is very simple. First, add the Gradle plugin as a
dependency in your build script.</p>
<div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">buildscript</span> <span class="o">{</span>
<span class="o">...</span>
<span class="n">dependencies</span> <span class="o">{</span>
<span class="o">...</span>
<span class="n">classpath</span> <span class="s1">'org.lucasr.probe:gradle-plugin:0.1.3'</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Then apply the plugin to your app’s <em>build.gradle</em>.</p>
<div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">apply</span> <span class="nl">plugin:</span> <span class="s1">'org.lucasr.probe'</span>
</code></pre></div></div>
<p>Probe’s proxy generation is disabled by default and needs to be explicitly
enabled on specific build variants (build type + product flavour). For example,
this is how you enable Probe proxies in debug builds.</p>
<div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">probe</span> <span class="o">{</span>
<span class="n">buildVariants</span> <span class="o">{</span>
<span class="n">debug</span> <span class="o">{</span>
<span class="n">enabled</span> <span class="o">=</span> <span class="kc">true</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>And that’s all! You should now be able to deploy interceptors on any part of
your UI. Here’s how you could deploy an <em>OvermeasureInterceptor</em> in an
activity.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">final</span> <span class="kd">class</span> <span class="nc">MainActivity</span> <span class="kd">extends</span> <span class="nc">Activity</span> <span class="o">{</span>
<span class="nd">@Override</span>
<span class="kd">protected</span> <span class="kt">void</span> <span class="nf">onCreate</span><span class="o">(</span><span class="nc">Bundle</span> <span class="n">savedInstanceState</span><span class="o">)</span> <span class="o">{</span>
<span class="nc">Probe</span><span class="o">.</span><span class="na">deploy</span><span class="o">(</span><span class="k">this</span><span class="o">,</span> <span class="k">new</span> <span class="nc">OvermeasureInterceptor</span><span class="o">());</span>
<span class="kd">super</span><span class="o">.</span><span class="na">onCreate</span><span class="o">(</span><span class="n">savedInstanceState</span><span class="o">);</span>
<span class="n">setContentView</span><span class="o">(</span><span class="no">R</span><span class="o">.</span><span class="na">id</span><span class="o">.</span><span class="na">main_activity</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>While working on this feature, I have changed <em>DexMaker</em> to be an optional
dependency i.e. you have to explicitly add <em>DexMaker</em> as a build dependency in
your app in order to use it.</p>
<p>This is my first Gradle plugin. There’s definitely a lot of room for
improvement here. These features are available in the 0.1.3 release in Maven
Central.</p>
<p>As usual, feedback, bug reports, and fixes are <a href="https://github.com/lucasr/probe/">very
welcome</a>. Enjoy!</p>Lucas Rochalucasr@lucasr.orgUp until now, Probe relied on dynamic view proxies generated at runtime to intercept View calls. Although very convenient, this approach greatly affects the time to inflate your layouts—which limits the number of use cases for the library, especially in more complex apps.New Features in Picasso2014-09-23T15:52:54+00:002014-09-23T15:52:54+00:00https://lucasr.org/2014/09/23/new-features-in-picasso<p>I’ve always been a big fan of <a href="https://github.com/square/picasso/">Picasso</a>,
the Android image loading library by the Square folks. It provides some
powerful features with a rather simple API.</p>
<p>Recently, I started working on a set of new features for Picasso that will make
it even more awesome: request handlers, request management, and request
priorities. These features have all been merged to the main repo now. Let me
give you a quick overview of what they enable you to do.</p>
<h3 id="request-handlers">Request Handlers</h3>
<p>Picasso supports a wide variety of image sources, from simple resources to
content providers, network, and more. Sometimes though, you need to load images
in unconventional ways that are not supported by default in Picasso.</p>
<p>Wouldn’t it be nice if you could easily integrate your custom image loading
logic with Picasso? That’s what the new <a href="https://github.com/square/picasso/pull/512">request
handlers</a> are about. All you need
to do is subclass <em>RequestHandler</em> and implement a couple of methods. For
example:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">PonyRequestHandler</span> <span class="kd">extends</span> <span class="nc">RequestHandler</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">String</span> <span class="no">PONY_SCHEME</span> <span class="o">=</span> <span class="s">"pony"</span><span class="o">;</span>
<span class="nd">@Override</span> <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">canHandleRequest</span><span class="o">(</span><span class="nc">Request</span> <span class="n">data</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="no">PONY_SCHEME</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">data</span><span class="o">.</span><span class="na">uri</span><span class="o">.</span><span class="na">getScheme</span><span class="o">());</span>
<span class="o">}</span>
<span class="nd">@Override</span> <span class="kd">public</span> <span class="nc">Result</span> <span class="nf">load</span><span class="o">(</span><span class="nc">Request</span> <span class="n">data</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="nf">Result</span><span class="o">(</span><span class="n">somePonyBitmap</span><span class="o">,</span> <span class="no">MEMORY</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Then you register your request handler when instantiating Picasso:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Picasso</span> <span class="n">picasso</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Picasso</span><span class="o">.</span><span class="na">Builder</span><span class="o">(</span><span class="n">context</span><span class="o">)</span>
<span class="o">.</span><span class="na">addRequestHandler</span><span class="o">(</span><span class="k">new</span> <span class="nc">PonyHandler</span><span class="o">())</span>
<span class="o">.</span><span class="na">build</span><span class="o">();</span>
</code></pre></div></div>
<p><em>Voilà</em>! Now Picasso can handle pony URIs:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">picasso</span><span class="o">.</span><span class="na">load</span><span class="o">(</span><span class="s">"pony://somePonyName"</span><span class="o">)</span>
<span class="o">.</span><span class="na">into</span><span class="o">(</span><span class="n">someImageView</span><span class="o">)</span>
</code></pre></div></div>
<p>This pull request also involved rewriting all built-in bitmap loaders on top of
the new API. This means you can also override the built-in request handlers if
you need to.</p>
<h3 id="request-management">Request Management</h3>
<p>Even though Picasso handles view recycling, it does so in an inefficient way.
For instance, if you do a fling gesture on a <em>ListView</em>, Picasso will keep
triggering and canceling requests blindly because there was no way to make it
pause/resume requests according to the user interaction. Not anymore!</p>
<p>The new <a href="https://github.com/square/picasso/pull/665">request management APIs</a>
allow you to tag associated requests that should be managed together. You can
then pause, resume, or cancel requests associated with specific tags. The first
thing you have to do is tag your requests as follows:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Picasso</span><span class="o">.</span><span class="na">with</span><span class="o">(</span><span class="n">context</span><span class="o">)</span>
<span class="o">.</span><span class="na">load</span><span class="o">(</span><span class="s">"http://example.com/image.jpg"</span><span class="o">)</span>
<span class="o">.</span><span class="na">tag</span><span class="o">(</span><span class="n">someTag</span><span class="o">)</span>
<span class="o">.</span><span class="na">into</span><span class="o">(</span><span class="n">someImageView</span><span class="o">);</span>
</code></pre></div></div>
<p>Then you can pause and resume requests with this tag based on, say, the scroll
state of a <em>ListView</em>. For example, Picasso’s sample app now has the following
scroll listener:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">SampleScrollListener</span> <span class="kd">implements</span> <span class="nc">AbsListView</span><span class="o">.</span><span class="na">OnScrollListener</span> <span class="o">{</span>
<span class="o">...</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">onScrollStateChanged</span><span class="o">(</span><span class="nc">AbsListView</span> <span class="n">view</span><span class="o">,</span> <span class="kt">int</span> <span class="n">scrollState</span><span class="o">)</span> <span class="o">{</span>
<span class="nc">Picasso</span> <span class="n">picasso</span> <span class="o">=</span> <span class="nc">Picasso</span><span class="o">.</span><span class="na">with</span><span class="o">(</span><span class="n">context</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">scrollState</span> <span class="o">==</span> <span class="no">SCROLL_STATE_IDLE</span> <span class="o">||</span>
<span class="n">scrollState</span> <span class="o">==</span> <span class="no">SCROLL_STATE_TOUCH_SCROLL</span><span class="o">)</span> <span class="o">{</span>
<span class="n">picasso</span><span class="o">.</span><span class="na">resumeTag</span><span class="o">(</span><span class="n">someTag</span><span class="o">);</span>
<span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
<span class="n">picasso</span><span class="o">.</span><span class="na">pauseTag</span><span class="o">(</span><span class="n">someTag</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">...</span>
<span class="o">}</span>
</code></pre></div></div>
<p>These APIs give you a much finer control over your image requests. The scroll
listener is just the canonical use case.</p>
<h3 id="request-priorities">Request Priorities</h3>
<p>It’s very common for images in your Android UI to have different priorities.
For instance, you may want to give higher priority to the big hero image in
your activity in relation to other secondary images in the same screen.</p>
<p>Up until now, there was no way to hint Picasso about the relative priorities
between images. The new <a href="https://github.com/square/picasso/pull/666">priority
API</a> allows you to tell Picasso
about the intended order of your image requests. You can just do:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Picasso</span><span class="o">.</span><span class="na">with</span><span class="o">(</span><span class="n">context</span><span class="o">)</span>
<span class="o">.</span><span class="na">load</span><span class="o">(</span><span class="s">"http://example.com/image.jpg"</span><span class="o">)</span>
<span class="o">.</span><span class="na">priority</span><span class="o">(</span><span class="no">HIGH</span><span class="o">)</span>
<span class="o">.</span><span class="na">into</span><span class="o">(</span><span class="n">someImageView</span><span class="o">);</span>
</code></pre></div></div>
<p>These priorities don’t guarantee a specific order, they just tilt the balance
in favour of higher-priority requests.</p>
<hr />
<p>That’s all for now. Big thanks to Jake Wharton and Dimitris Koutsogiorgas for
the prompt code and API reviews!</p>
<p>You can try these new APIs now by fetching the <a href="https://github.com/square/picasso/">latest Picasso
code</a> on Github. These features will
probably be available in the 2.4 release. Enjoy!</p>Lucas Rochalucasr@lucasr.orgI’ve always been a big fan of Picasso, the Android image loading library by the Square folks. It provides some powerful features with a rather simple API.Introducing Probe2014-09-16T10:32:49+00:002014-09-16T10:32:49+00:00https://lucasr.org/2014/09/16/introducing-probe<p>We’ve all heard of the best practices regarding layouts on Android: keep your
view tree as simple as possible, avoid multi-pass layouts high up in the
hierarchy, etc. But the truth is, it’s pretty hard to <em>see</em> what’s actually
going on in your view tree in each UI traversal (measure → layout → draw).</p>
<p>We’re well served with developer options for tracking graphics
performance—debug GPU overdraw, show hardware layers updates, profile GPU
rendering, and others. However, there is a big gap in terms of development
tools for tracking layout traversals and figuring out how your layouts actually
behave. This is why I created <a href="https://github.com/lucasr/probe/">Probe</a>.</p>
<p>Probe is a small library that allows you to intercept view method calls during
Android’s layout traversals e.g. <em>onMeasure()</em>, <em>onLayout()</em>, <em>onDraw()</em>, etc.
Once a method call is intercepted, you can either do extra things on top of the
view’s original implementation or completely override the method on-the-fly.</p>
<p>Using Probe is super simple. All you have to do is implement an <em>Interceptor</em>.
Here’s an interceptor that completely overrides a view’s onDraw(). Calling
super.onDraw() would call the view’s original implementation.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">DrawGreen</span> <span class="kd">extends</span> <span class="nc">Interceptor</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="nc">Paint</span> <span class="n">mPaint</span><span class="o">;</span>
<span class="kd">public</span> <span class="nf">DrawGreen</span><span class="o">()</span> <span class="o">{</span>
<span class="n">mPaint</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Paint</span><span class="o">();</span>
<span class="n">mPaint</span><span class="o">.</span><span class="na">setColor</span><span class="o">(</span><span class="nc">Color</span><span class="o">.</span><span class="na">GREEN</span><span class="o">);</span>
<span class="o">}</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">onDraw</span><span class="o">(</span><span class="nc">View</span> <span class="n">view</span><span class="o">,</span> <span class="nc">Canvas</span> <span class="n">canvas</span><span class="o">)</span> <span class="o">{</span>
<span class="n">canvas</span><span class="o">.</span><span class="na">drawPaint</span><span class="o">(</span><span class="n">mPaint</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Then deploy your Interceptor by inflating your layout with a Probe:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Probe</span> <span class="n">probe</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Probe</span><span class="o">(</span>
<span class="k">this</span><span class="o">,</span> <span class="k">new</span> <span class="nc">DrawGreen</span><span class="o">(),</span> <span class="k">new</span> <span class="nc">Filter</span><span class="o">.</span><span class="na">ViewId</span><span class="o">(</span><span class="no">R</span><span class="o">.</span><span class="na">id</span><span class="o">.</span><span class="na">view2</span><span class="o">));</span>
<span class="nc">View</span> <span class="n">root</span> <span class="o">=</span> <span class="n">probe</span><span class="o">.</span><span class="na">inflate</span><span class="o">(</span><span class="no">R</span><span class="o">.</span><span class="na">layout</span><span class="o">.</span><span class="na">main_activity</span><span class="o">,</span> <span class="kc">null</span><span class="o">);</span>
</code></pre></div></div>
<p>Just to give you an idea of the kind of things you can do with Probe, I’ve
already implemented a couple of built-in interceptors.
<a href="https://github.com/lucasr/probe/blob/master/library/src/main/java/org/lucasr/probe/interceptors/OvermeasureInterceptor.java">OvermeasureInterceptor</a>
tints views according to the number of times they got measured in a single
traversal i.e. equivalent to overdraw but for measurement.</p>
<p><a href="https://github.com/lucasr/probe/blob/master/library/src/main/java/org/lucasr/probe/interceptors/LayoutBoundsInterceptor.java">LayoutBoundsInterceptor</a>
is equivalent to Android’s “Show layout bounds” developer option. The main
difference is that you can show bounds only for specific views.</p>
<p>Under the hood, Probe uses Google’s
<a href="https://code.google.com/p/dexmaker/">DexMaker</a> to generate dynamic View
proxies during layout inflation. The stock
<a href="http://dexmaker.googlecode.com/git/javadoc/com/google/dexmaker/stock/ProxyBuilder.html">ProxyBuilder</a>
implementation was not good enough for Probe because I wanted to avoid using
reflection entirely after the proxy classes were generated. So I created a
specialized <a href="https://github.com/lucasr/probe/blob/master/library/src/main/java/org/lucasr/probe/ViewProxyBuilder.java">View proxy
builder</a>
that generates proxy classes tailored for Probe’s use case.</p>
<p>This means Probe takes longer than your usual <em>LayoutInflater</em> to inflate
layout resources. There’s no use of reflection after layout inflation though.
Your views should perform the same. For now, Probe is meant to be a developer
tool only and I don’t recommend using it in production.</p>
<p>The <a href="https://github.com/lucasr/probe/">code</a> is available on Github. As usual,
contributions are very welcome.</p>Lucas Rochalucasr@lucasr.orgWe’ve all heard of the best practices regarding layouts on Android: keep your view tree as simple as possible, avoid multi-pass layouts high up in the hierarchy, etc. But the truth is, it’s pretty hard to see what’s actually going on in your view tree in each UI traversal (measure → layout → draw).Introducing dspec2014-09-08T13:52:02+00:002014-09-08T13:52:02+00:00https://lucasr.org/2014/09/08/introducing-dspec<p>With all the recent focus on <a href="http://www.google.com/design/spec/layout/metrics-and-keylines.html">baseline grids, keylines, and spacing
markers</a>
from Android’s material design, I found myself wondering how I could make it
easier to check the correctness of my Android UI implementation against the
intended spec.</p>
<p>Wouldn’t it be nice if you could easily provide the spec values as input and
get it rendered on top of your UI for comparison? Enter
<a href="https://github.com/lucasr/dspec">dspec</a>, a super simple way to define UI specs
that can be rendered on top of Android UIs.</p>
<p>Design specs can be defined either programmatically through a <a href="https://github.com/lucasr/dspec/blob/master/library/src/main/java/org/lucasr/dspec/DesignSpec.java">simple
API</a>
or via <a href="https://github.com/lucasr/dspec/blob/master/sample/src/main/res/raw/main_activity_spec.json">JSON
files</a>.
Specs can define various aspects of the baseline grid, keylines, and spacing
markers such as visibility, offset, size, color, etc.</p>
<picture class="figure">
<img class="figure-img img-fluid " src="/wp-content/uploads/2014/09/2014-09-08-13.02.51.png" title="Baseline grid, keylines, and spacing markers in action." />
<figcaption class="figure-caption">Baseline grid, keylines, and spacing markers in action.</figcaption>
</picture>
<p>Given the responsive nature of Android UIs, the keylines and spacing markers
are positioned in relation to predefined reference points (e.g. left, right,
vertical center, etc) instead of absolute offsets.</p>
<p>The JSON files are Android resources which means you can easily adapt the spec
according to different form factors e.g. different specs for phones and
tablets. The JSON specs provide a simple way for designers to communicate their
intent in a computer-readable way.</p>
<p>You can integrate a <em>DesignSpec</em> with your custom views by drawing it in your
<em>View</em>’s <em>onDraw(Canvas)</em> method. But the simplest way to draw a spec on
top of a view is to enclose it in a
<a href="https://github.com/lucasr/dspec/blob/master/library/src/main/java/org/lucasr/dspec/DesignSpecFrameLayout.java">DesignSpecFrameLayout</a>—which
can take an <em>designSpec</em> XML attribute pointing to the spec resource. For
example:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><DesignSpecFrameLayout</span>
<span class="na">android:layout_width=</span><span class="s">"match_parent"</span>
<span class="na">android:layout_height=</span><span class="s">"match_parent"</span>
<span class="na">app:designSpec=</span><span class="s">"@raw/my_spec"</span><span class="nt">></span>
...
<span class="nt"></DesignSpecFrameLayout></span>
</code></pre></div></div>
<p>I can’t wait to start using <em>dspec</em> in some of the new UI work we’re doing
<a href="https://play.google.com/store/apps/details?id=org.mozilla.firefox">Firefox for
Android</a>
now. I hope you find it useful too. The <a href="https://github.com/lucasr/dspec">code is
available</a> on Github. As usual, testing and
fixes are very welcome. Enjoy!</p>Lucas Rochalucasr@lucasr.orgWith all the recent focus on baseline grids, keylines, and spacing markers from Android’s material design, I found myself wondering how I could make it easier to check the correctness of my Android UI implementation against the intended spec.The new TwoWayView2014-07-31T11:33:17+00:002014-07-31T11:33:17+00:00https://lucasr.org/2014/07/31/the-new-twowayview<p><em>What if writing custom view recycling layouts was a lot simpler?</em> This
question stuck in my mind since I started writing Android apps a few years ago.</p>
<p>The lack of proper extension hooks in the <em>AbsListView</em> API has been one of my
biggest pain points on Android. The community has come up with different layout
implementations that were largely based on <em>AbsListView</em>’s code but none of
them really solved the framework problem.</p>
<p>So a few months ago, I finally set to work on a new API for
<a href="https://github.com/lucasr/twoway-view/"><em>TwoWayView</em></a> that would provide a
framework for custom view recycling layouts. I had made some good
<a href="https://plus.google.com/+LucasRocha/posts/WBaryNqAHiy">progress</a> but then
Google announced <em>RecyclerView</em> at I/O and everything changed.</p>
<p>At first sight, <em>RecyclerView</em> seemed to be an exact overlap with the new
<em>TwoWayView</em> API. After some digging though, it became clear that
<em>RecyclerView</em> was a superset of what I was working on. So I decided to embrace
<em>RecyclerView</em> and rebuild <em>TwoWayView</em> on top of it.</p>
<p>The new <em>TwoWayView</em> is functional enough now. Time to get some early feedback.
This post covers the upcoming API and the general-purpose layout managers that
will ship with it.</p>
<h2 id="creating-your-own-layouts">Creating your own layouts</h2>
<p><em>RecyclerView</em> itself doesn’t actually do much. It implements the fundamental
state handling around child views, touch events and adapter changes, then
delegates the actual behaviour to separate components—<em>LayoutManager</em>,
<em>ItemDecoration</em>, <em>ItemAnimator</em>, etc. This means that you still have
to write some non-trivial code to create your own layouts.</p>
<p><em>LayoutManager</em> is a low-level API. It simply gives you extension points to
handle scrolling and layout. For most layouts, the general structure of a
<em>LayoutManager</em> implementation is going to be very similar—recycle views out of
parent bounds, add new views as the user scrolls, layout scrap list items, etc.</p>
<p>Wouldn’t it be nice if you could implement <em>LayoutManagers</em> with a higher-level
API that was more focused on the layout itself? Enter the new <em>TwoWayView</em> API.</p>
<p><em>TwoWayLayoutManager</em><sup><a href="https://github.com/lucasr/twoway-view/blob/master/core/src/main/java/org/lucasr/twowayview/TWAbsLayoutManager.java">code</a></sup>
is a simple API on top of <em>LayoutManager</em> that does all the laborious work for
you so that you can focus on how the child views are measured, placed, and
detached from the <em>RecyclerView</em>.</p>
<p>To get a better idea of what the API looks like, have a look at these sample
layouts:
<a href="https://gist.github.com/lucasr/391fbcb04479ebbe838c">SimpleListLayout</a> is a
list layout and
<a href="https://gist.github.com/lucasr/8ea1832239dcc5119e77">GridAndListLayout</a> is a
more complex example where the first N items are laid out as a grid and the
remaining ones behave like a list. As you can see you only need to override a
couple of simple methods to create your own layouts.</p>
<h2 id="built-in-layouts">Built-in layouts</h2>
<p>The new API is pretty nice but I also wanted to create a space for
collaboration around general-purpose layout managers. So far, Google has only
provided <em>LinearLayoutManager</em>. They might end up releasing a few more layouts
later this year but, for now, that is all we got.</p>
<picture class="figure">
<img class="figure-img img-fluid " src="/wp-content/uploads/2014/07/layouts.png" title="Built-in layouts: List, Grid, Staggered Grid, and Spannable Grid" />
<figcaption class="figure-caption">Built-in layouts: List, Grid, Staggered Grid, and Spannable Grid</figcaption>
</picture>
<p>The new <em>TwoWayView</em> ships with a collection of four built-in layouts: <em>List</em>,
<em>Grid</em>, <em>Staggered Grid</em>, and <em>Spannable Grid</em>.</p>
<p>These layouts support all <em>RecyclerView</em> features: item animations,
decorations, scroll to position, smooth scroll to position, view state
saving, etc. They can all be scrolled vertically and horizontally—this is
the <em>TwoWayView</em> project after all ;-)</p>
<p>You probably know how the <em>List</em> and <em>Grid</em> layouts work. <em>Staggered Grid</em>
arranges items with variable heights or widths into different columns or rows
according to its orientation.</p>
<p><em>Spannable Grid</em> is a grid layout with fixed-size cells that allows items to
span multiple columns and rows. You can define the column and row spans as
attributes in the child views as shown below.</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><FrameLayout</span>
<span class="na">android:layout_width=</span><span class="s">"match_parent"</span>
<span class="na">android:layout_height=</span><span class="s">"match_parent"</span>
<span class="na">app:colSpan=</span><span class="s">"2"</span>
<span class="na">app:rowSpan=</span><span class="s">"3"</span><span class="nt">></span>
...
<span class="nt"></FrameLayout></span>
</code></pre></div></div>
<h2 id="utilities">Utilities</h2>
<p>The new <em>TwoWayView</em> API will ship with a convenience view (<em>TwoWayView</em>) that
can take a <em>layoutManager</em> XML attribute that points to a layout manager class.</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><org.lucasr.twowayview.widget.TwoWayView</span>
<span class="na">android:layout_width=</span><span class="s">"match_parent"</span>
<span class="na">android:layout_height=</span><span class="s">"match_parent"</span>
<span class="na">app:layoutManager=</span><span class="s">"ListLayoutManager"</span><span class="nt">/></span>
</code></pre></div></div>
<p>This way you can leverage the resource system to set layout manager depending
on device features and configuration via styles.</p>
<p>You can also use <em>ItemClickSupport</em> to use <em>ListView</em>-style item (long) click
listeners. You can easily plug-in support for those in any <em>RecyclerView</em> (see
<a href="https://gist.github.com/lucasr/e7417474278ca0dd7783">sample</a>).</p>
<p>I’m also planning to create pluggable item decorations for dividers, item
spacing, list selectors, and more.</p>
<hr />
<p>That’s all for now! The API is still in flux and will probably go through a few
more iterations. The built-in layouts definitely need more testing.</p>
<p>You can help by filing (and fixing) bugs and giving feedback on the API. Maybe
try using the built-in layouts in your apps and see what happens?</p>
<p>I hope <em>TwoWayView</em> becomes a productive collaboration space for <em>RecyclerView
extensions and layouts</em>.
<a href="https://github.com/lucasr/twoway-view/">Contributions</a> are very welcome!</p>Lucas Rochalucasr@lucasr.orgWhat if writing custom view recycling layouts was a lot simpler? This question stuck in my mind since I started writing Android apps a few years ago.Custom Layouts on Android2014-05-12T12:42:06+00:002014-05-12T12:42:06+00:00https://lucasr.org/2014/05/12/custom-layouts-on-android<p>If you ever built an Android app, you have definitely used some of the built-in
layouts available in the platform—<em>RelativeLayout</em>, <em>LinearLayout</em>,
<em>FrameLayout</em>, etc. They are our bread and butter for building Android
UIs.</p>
<p>The built-in layouts combined provide a powerful toolbox for implementing
complex UIs. But there will still be cases where the design of your app will
require you to implement custom layouts.</p>
<p>There are two main reasons to go custom. First, to make your UI more
efficient—by reducing the number of views and/or making faster layout
traversals. Second, when you are building UIs that are not naturally possible
to implement with stock widgets.</p>
<p>In this post, I will demonstrate four different ways of implementing custom
layouts and discuss their respective pros and cons: composite view, custom
composite view, flat custom view, and async custom views.</p>
<p>The code samples are available in my
<a href="https://github.com/lucasr/android-layout-samples">android-layout-samples</a>
repo. This app implements the same UI with each technique discussed here and
uses <a href="http://square.github.io/picasso/">Picasso</a> for image loading. The UI is a
simplified version of Twitter app’s stream—no interactions, just the layouts.</p>
<p>Ok, let’s start with the most common type of custom layout: <em>composite view</em>.</p>
<h3 id="composite-view">Composite View</h3>
<p>This is usually your starting point. <em>Composite views</em> (also known as compound
views) are the easiest way of combining multiple views into a reusable
UI component. They are very simple to implement:</p>
<ol>
<li>Subclass one of the built-in layouts.</li>
<li>Inflate a <em>merge</em> layout in the constructor.</li>
<li>Initialize members to point to inner views with <em>findViewById()</em>.</li>
<li>Add your own APIs to query and update the view state.</li>
</ol>
<p><em>TweetCompositeView</em><a href="https://github.com/lucasr/android-layout-samples/blob/master/src/main/java/org/lucasr/layoutsamples/widget/TweetCompositeView.java"><sup>code</sup></a>
is a composite view. It subclasses <em>RelativeLayout</em>, inflates
<em>tweet_composite_layout.xml</em><a href="https://github.com/lucasr/android-layout-samples/blob/master/src/main/res/layout/tweet_composite_view.xml"><sup>code</sup></a>
into it, and exposes an <em>update()</em> method to refresh its state in the
adapter<a href="https://github.com/lucasr/android-layout-samples/blob/master/src/main/java/org/lucasr/layoutsamples/adapter/TweetsAdapter.java#L85"><sup>code</sup></a>.
Simple stuff.</p>
<h3 id="custom-composite-view">Custom Composite View</h3>
<p><em>TweetCompositeView</em> will likely perform fairly well in most situations. But,
for sake of argument, suppose you want to reduce the
number of child views and make layout traversals a bit
more efficient.</p>
<p>Although composite views are simple to implement, using general-purpose layouts
has a cost—especially with complex containers like <em>LinearLayout</em> and
<em>RelativeLayout</em>. As platform building blocks, they have to handle tons of
layout combinations and might have to measure child views multiple times in a
single traversal—<em>LinearLayout</em>’s <em>layout_weight</em> being a common example.</p>
<p>You can greatly optimize your UI by implementing a custom logic for measuring
and positioning child views that is specially tailored for your app. This is
what I like to call a <em>custom composite view</em>.</p>
<p>A custom composite view is simply a composite view that overrides <em>onMeasure()</em>
and <em>onLayout()</em>. So, instead of subclassing an existing container like
<em>RelativeLayout</em>, you will be subclassing the more general <em>ViewGroup</em>.</p>
<p><em>TweetLayoutView</em><a href="https://github.com/lucasr/android-layout-samples/blob/master/src/main/java/org/lucasr/layoutsamples/widget/TweetLayoutView.java"><sup>code</sup></a>
implements this technique. Note how it gets rid of the <em>LinearLayout</em> from
<em>TweetComposiveView</em> and avoids the use of <em>layout_weight</em>
altogether<a href="https://github.com/lucasr/android-layout-samples/blob/master/src/main/res/layout/tweet_layout_view.xml"><sup>code</sup></a>.</p>
<p>The laborious work of figuring out what
<a href="http://developer.android.com/reference/android/view/View.MeasureSpec.html"><em>MeasureSpec</em></a>
to use on each child view is done by the ViewGroup’s handy
<a href="http://developer.android.com/reference/android/view/ViewGroup.html#measureChildWithMargins%28android.view.View,%20int,%20int,%20int,%20int%29"><em>measureChildWithMargins()</em></a>
method—and
<a href="http://developer.android.com/reference/android/view/ViewGroup.html#getChildMeasureSpec%28int,%20int,%20int%29"><em>getChildMeasureSpec()</em></a>
under the hood.</p>
<p><em>TweetLayoutView</em> probably doesn’t handle all possible layout combinations
correctly but it doesn’t have to. It is absolutely fine to optimize custom
layouts for your specific needs. This allows you to write both simpler and more
efficient layout code for your app.</p>
<h3 id="flat-custom-view">Flat Custom View</h3>
<p>As you can see, custom composite views are fairly simple to write using
<em>ViewGroup</em> APIs. Most of the time, they will give you the performance your app
needs.</p>
<p>However, you might want to go further and optimize your layouts even more on
critical parts of your UI that are very dynamic e.g. <em>ListViews</em>, <em>ViewPager,</em>
etc. What about merging all <em>TweetLayoutView</em> child views into a single custom
view to rule them all? That is what <em>flat custom views</em> are about—see image
below.</p>
<picture class="figure">
<img class="figure-img img-fluid " src="/wp-content/uploads/2014/05/layouts.png" title="Custom Composite View (left) and Flat Custom View (right)" />
<figcaption class="figure-caption">Custom Composite View (left) and Flat Custom View (right)</figcaption>
</picture>
<p>A flat custom view is a fully custom view that measures, arranges, and draws
its inner elements. So you will be subclassing <em>View</em> instead of <em>ViewGroup</em>.</p>
<p>If you are looking for real-world examples, enable the “show layout
bounds” developer option in your device and have a look at apps like
Twitter, GMail, and Pocket. They all use flat custom views in their listing
UIs.</p>
<p>The main benefit from using flat custom views is the great potential for flattening your view hierarchy, resulting in faster traversals and, potentially, a reduced memory footprint.</p>
<p>Flat custom views give you maximum freedom as they are literally a blank
canvas. But this comes at a price: you won’t be able to use the feature-packed
stock widgets such as <em>TextView</em> and <em>ImageView</em>. Yes, it is simple to <a href="http://developer.android.com/reference/android/graphics/Canvas.html#drawText%28java.lang.String,%20float,%20float,%20android.graphics.Paint%29">draw
text</a>
on a <em>Canvas</em> but what about
<a href="http://developer.android.com/reference/android/widget/TextView.html#setEllipsize%28android.text.TextUtils.TruncateAt%29">ellipsizing</a>?
Yes, you can easily <a href="http://developer.android.com/reference/android/graphics/Canvas.html#drawBitmap%28int[],%20int,%20int,%20float,%20float,%20int,%20int,%20boolean,%20android.graphics.Paint%29">draw a
bitmap</a>
but what about <a href="http://developer.android.com/reference/android/widget/ImageView.html#attr_android:scaleType">scaling
modes</a>?
Same applies to touch events, accessibility, keyboard navigation, etc.</p>
<p>The bottom line is: with flat custom views,you will likely have to re-implement
features that you would get for free from the platform. So you should only
consider using them on core parts of your UI. For all other cases, just lean on
the platform with composite views, custom or not.</p>
<p><em>TweetElementView</em><a href="https://github.com/lucasr/android-layout-samples/blob/master/src/main/java/org/lucasr/layoutsamples/widget/TweetElementView.java"><sup>code</sup></a>
is a flat custom view. To make it easier to implement it, I created a little
custom view framework called <em>UIElement</em>. You will find it in the
<em>canvas</em><a href="https://github.com/lucasr/android-layout-samples/tree/master/src/main/java/org/lucasr/layoutsamples/canvas"><sup>code</sup></a>
package.</p>
<p>The <em>UIElement</em> framework provides a measure/layout API which is analogous to
Android’s. It contains headless versions of <em>TextView</em> and <em>ImageView</em> with
only the necessary features for this demo—see
<em>TextElement</em><a href="https://github.com/lucasr/android-layout-samples/blob/master/src/main/java/org/lucasr/layoutsamples/canvas/TextElement.java"><sup>code</sup></a>
and
<em>ImageElement</em><a href="https://github.com/lucasr/android-layout-samples/blob/master/src/main/java/org/lucasr/layoutsamples/canvas/ImageElement.java"><sup>code</sup></a>
respectively. It also has its own
inflater<a href="https://github.com/lucasr/android-layout-samples/blob/master/src/main/java/org/lucasr/layoutsamples/canvas/UIElementInflater.java"><sup>code</sup></a>
to instantiate <em>UIElements</em> from layout resource
files<a href="https://github.com/lucasr/android-layout-samples/blob/master/src/main/res/layout/tweet_element_view.xml"><sup>code</sup></a>.</p>
<p>Probably worth noting: the <em>UIElement</em> framework is in a very early development
stage. Consider it a very rough sketch of something that might actually become
useful in the future.</p>
<p>You have probably noticed how simple <em>TweetElementView</em> looks. This is because
the real code is all in
<em>TweetElement</em><a href="https://github.com/lucasr/android-layout-samples/blob/master/src/main/java/org/lucasr/layoutsamples/widget/TweetElement.java"><sup>code</sup></a>—with
<em>TweetElementView</em> just acting as a
host<a href="https://github.com/lucasr/android-layout-samples/blob/master/src/main/java/org/lucasr/layoutsamples/canvas/UIElementHost.java"><sup>code</sup></a>.</p>
<p>The layout code in <em>TweetElement</em> is pretty much analogous to
<em>TweetLayoutView</em>’s. It handles Picasso requests
differently<a href="https://github.com/lucasr/android-layout-samples/blob/master/src/main/java/org/lucasr/layoutsamples/widget/ImageElementTarget.java"><sup>code</sup></a>
because it doesn’t use <em>ImageViews</em>.</p>
<h3 id="async-custom-view">Async Custom View</h3>
<p>As we all know, the Android UI framework is
<a href="http://developer.android.com/guide/components/processes-and-threads.html#Threads">single-threaded</a>.
And this is for a good reason: UI toolkits are not your simplest piece of code.
Making them thread-safe and asynchronous would be an unworthy herculean effort.</p>
<p>This single-threaded nature leads to some fundamental limitations. It means,
for example, that you can’t do layout traversals off main thread at
all—something that would be useful in very complex and dynamic UIs.</p>
<p>For example, if your app has complex items in a <em>ListView</em> (like most social
apps do), you will probably end up skipping frames while scrolling
because <em>ListView</em> has to
measure<a href="https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/widget/ListView.java#L1870"><sup>code</sup></a>
and
layout<a href="https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/widget/ListView.java#L1882"><sup>code</sup></a>
each child view to account for their new content as they become visible. The
same issue applies to <em>GridViews</em>, <em>ViewPagers</em>, and the like.</p>
<p>Wouldn’t it be nice if we could do a layout traversal on the child views that
are not visible yet without blocking the main thread? This way, the <em>measure()</em>
and <em>layout()</em> calls on child views would take no time when needed in the UI
thread.</p>
<p>Enter <em>async custom view</em>, an experiment to allow layout passes to happen off
main thread. This is inspired by the <a href="https://www.youtube.com/watch?v=TCuVxU07NWs&feature=youtu.be&t=33m43s">async node
framework</a>
developed by the Paper team at Facebook.</p>
<p>Given that we can never ever touch the UI toolkit off main thread, we need an
API that is able to measure/layout the contents of a view without being
directly coupled to it. This is exactly what the <em>UIElement</em> framework provides
us.</p>
<p><em>AsyncTweetView</em><a href="https://github.com/lucasr/android-layout-samples/blob/master/src/main/java/org/lucasr/layoutsamples/async/AsyncTweetView.java"><sup>code</sup></a>
is an async custom view. It uses a
thread-safe <em>AsyncTweetElement</em><a href="https://github.com/lucasr/android-layout-samples/blob/master/src/main/java/org/lucasr/layoutsamples/async/AsyncTweetElement.java"><sup>code</sup></a>
factory<a href="https://github.com/lucasr/android-layout-samples/blob/master/src/main/java/org/lucasr/layoutsamples/async/AsyncTweetElementFactory.java"><sup>code</sup></a>
to define its contents. Off-screen <em>AsyncTweetElement</em> instances are created,
pre-measured, and cached in memory from a background thread using a
<a href="https://github.com/lucasr/smoothie">Smoothie</a> item
loader<a href="https://github.com/lucasr/android-layout-samples/blob/master/src/main/java/org/lucasr/layoutsamples/async/TweetsLayoutLoader.java"><sup>code</sup></a>.</p>
<p>I had to compromise the async behaviour a bit because there’s no sane way of
showing layout placeholders on list items with arbitrary heights i.e. you end
up resizing them once the layout gets delivered asynchronously. So whenever an
<em>AsyncTweetView</em> is about to be displayed and it doesn’t find a matching
<em>AsyncTweetElement</em> in memory, it will force its creation in the UI
thread<a href="https://github.com/lucasr/android-layout-samples/blob/master/src/main/java/org/lucasr/layoutsamples/async/AsyncTweetView.java#L46"><sup>code</sup></a>.</p>
<p>Furthermore, both the preloading logic and the memory cache expiration would
need to be a lot smarter to ensure more layout cache hits in the UI thread. For
instance, using a LRU
cache<a href="https://github.com/lucasr/android-layout-samples/blob/master/src/main/java/org/lucasr/layoutsamples/async/UIElementCache.java"><sup>code</sup></a>
here doesn’t seem ideal.</p>
<p>Despite these limitations, the preliminary results from async custom views look
very promising. I’ll continue the explorations in this area by refining the
<em>UIElement</em> framework and using it in other kinds of UIs. Let’s see where it
goes.</p>
<h3 id="wrapping-up">Wrapping up</h3>
<p>When it comes to layouts, the more custom you go, the less you’ll be able to
lean on the platform’s proven components. So avoid premature optimization and
only go fully custom on areas that will actually affect the perceived quality
and performance of your app.</p>
<p>This is not a black-and-white decision though. Between stock widgets and fully
custom views there’s a wide spectrum of solutions—from simple composite views
to the more complex async views. In practice, you’ll usually end up combining
more than one of the techniques demonstrated here.</p>Lucas Rochalucasr@lucasr.orgIf you ever built an Android app, you have definitely used some of the built-in layouts available in the platform—RelativeLayout, LinearLayout, FrameLayout, etc. They are our bread and butter for building Android UIs.How Android Transitions Work2014-03-13T14:15:26+00:002014-03-13T14:15:26+00:00https://lucasr.org/2014/03/13/how-android-transitions-work<p>One of the biggest highlights of the Android KitKat release was the new
<a href="https://developer.android.com/about/versions/kitkat.html#44-transitions">Transitions framework</a>
which provides a very convenient API to animate UIs between different states.</p>
<p>The Transitions framework got me curious about how it orchestrates layout
rounds and animations between UI states. This post documents my understanding
of how transitions are implemented in Android after skimming through the source
code for a bit. I’ve sprinkled a bunch of source code links throughout the post
to make it easier to follow.</p>
<p>Although this post does contain a few development tips, this is <em>not</em> a
tutorial on how to use transitions in your apps. If that’s what you’re looking
for, I recommend reading Mark Allison’s <a href="http://blog.stylingandroid.com/archives/category/animation/transition">tutorials</a>
on the topic.</p>
<p>With that said, let’s get started.</p>
<h3 id="the-framework">The framework</h3>
<p>Android’s Transitions framework is essentially a mechanism for animating
layout changes e.g. adding, removing, moving, resizing, showing, or hiding
views.</p>
<p>The framework is built around three core entities: <em>scene root</em>,
<em>scenes</em>, and <em>transitions</em>. A <em>scene root</em> is an
ordinary <em>ViewGroup</em> that defines the piece of the UI on which the
transitions will run. A <em>scene</em> is a thin wrapper around a
<em>ViewGroup</em> representing a specific layout state of the <em>scene
root</em>.</p>
<p>Finally, and most importantly, a <em>transition</em> is the component
responsible for capturing layout differences and generating animators to switch
UI states. The execution of any transition always follows these steps:</p>
<ol>
<li>Capture start state</li>
<li>Perform layout changes</li>
<li>Capture end state</li>
<li>Run animators</li>
</ol>
<p>The process as a whole is managed by the <em>TransitionManager</em> but most
of the steps above (except for step 2) are performed by the
<em>transition</em>. Step 2 might be either a scene switch or an arbitrary
layout change.
**</p>
<h3 id="how-it-works">How it works</h3>
<p>Let’s take the simplest possible way of triggering a transition and go through
what happens under the hood. So, here’s a little code sample:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">TransitionManager</span><span class="o">.</span><span class="na">beginDelayedTransition</span><span class="o">(</span><span class="n">sceneRoot</span><span class="o">);</span>
<span class="nc">View</span> <span class="n">child</span> <span class="o">=</span> <span class="n">sceneRoot</span><span class="o">.</span><span class="na">findViewById</span><span class="o">(</span><span class="no">R</span><span class="o">.</span><span class="na">id</span><span class="o">.</span><span class="na">child</span><span class="o">);</span>
<span class="nc">LayoutParams</span> <span class="n">params</span> <span class="o">=</span> <span class="n">child</span><span class="o">.</span><span class="na">getLayoutParams</span><span class="o">();</span>
<span class="n">params</span><span class="o">.</span><span class="na">width</span> <span class="o">=</span> <span class="mi">150</span><span class="o">;</span>
<span class="n">params</span><span class="o">.</span><span class="na">height</span> <span class="o">=</span> <span class="mi">25</span><span class="o">;</span>
<span class="n">child</span><span class="o">.</span><span class="na">setLayoutParams</span><span class="o">(</span><span class="n">params</span><span class="o">);</span>
</code></pre></div></div>
<p>This code triggers an <em>AutoTransition</em> on the given <em>scene root</em>
animating <em>child</em> to its new size.</p>
<p>The first thing the <em>TransitionManager</em> will do in
<em>beingDelayedTransition()</em> is checking if there is a pending delayed
transition on the same <em>scene root</em> and just bail if there is one<sup><a href="https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/transition/TransitionManager.java#L356">code</a></sup>.
This means only the first <em>beingDelayedTransition()</em> call within the
same rendering frame will take effect.</p>
<p>Next, it will reuse a static <em><a href="https://developer.android.com/reference/android/transition/AutoTransition.html">AutoTransition</a></em>
instance<sup><a href="https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/transition/TransitionManager.java#L363">code</a></sup>.
You could also provide your own transition using a <a href="http://developer.android.com/reference/android/transition/TransitionManager.html#beginDelayedTransition%28android.view.ViewGroup,%20android.transition.Transition%29">variant</a>
of the same method. In any case, it will always clone<sup><a href="https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/transition/TransitionManager.java#L365">code</a></sup>
the given transition instance to ensure a fresh start<sup><a href="https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/transition/Transition.java#L1466">code</a></sup>—consequently
allowing you to safely reuse <em>Transition</em> instances across
<em>beingDelayedTransition()</em> calls.</p>
<p>It then moves on to capturing the start state<sup><a href="https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/transition/TransitionManager.java#L266">code</a></sup>.
If you set target view IDs on your transition, it will only capture values for
those<sup><a href="https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/transition/Transition.java#L1006">code</a></sup>.
Otherwise it will capture the start state recursively for all views under the
<em>scene root</em><sup><a href="https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/transition/TransitionManager.java#L366">code</a></sup>.
So, please, set target views on all your transitions, especially if your
<em>scene root</em> is a deep container with tons of children.</p>
<p>An interesting detail here: the state capturing code in <em>Transition</em> has
especial treatment for <em>ListViews</em> using adapters with stable IDs<sup><a href="https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/transition/TransitionManager.java#L366">code</a></sup>.
It will mark the <em>ListView</em> children as having <a href="http://developer.android.com/reference/android/view/View.html#setHasTransientState%28boolean%29">transient
state</a> to avoid them to be recycled during the transition. This means you
can very easily perform transitions when adding or removing items to/from a
<em>ListView</em>. Just call <em>beginDelayedTransition()</em> before updating
your adapter and an <em>AutoTransition</em> will do the magic for you—see this
<a href="https://gist.github.com/lucasr/9508647">gist</a> for a quick sample.</p>
<p>The state of each view taking part in a transition is stored in
<em>TransitionValues</em> instances which are essentially a <em>Map</em> with
an associated <em>View</em><sup><a href="https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/transition/TransitionValues.java">code</a></sup>.
This is one part of the API that feels a bit hand wavy.
Maybe <em>TransitionValues</em> should have been better encapsulated?</p>
<p><em><a href="http://developer.android.com/reference/android/transition/Transition.html">Transition</a></em>
subclasses fill the <em>TransitionValues</em> instances with the bits of the
<em>View</em> state that they’re interested in. The <em>ChangeBounds</em>
transition, for example, will capture the view bounds (left, top, right,
bottom) and location on screen<sup><a href="https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/transition/ChangeBounds.java#L84">code</a></sup>.</p>
<p>Once the start state is fully captured, <em>beginDelayedTransition()</em> will
exit whatever previous scene was set in the <em>scene root</em><sup><a href="https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/transition/TransitionManager.java#L272">code</a></sup>,
set current scene to <em>null</em><sup><a href="https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/transition/TransitionManager.java#L367">code</a></sup>
(as this is not a scene switch), and finally wait for the next rendering
frame<sup><a href="https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/transition/TransitionManager.java#L210">code</a></sup>.</p>
<p><em>TransitionManager</em> waits for the next rendering frame by adding an <a href="http://developer.android.com/reference/android/view/ViewTreeObserver.OnPreDrawListener.html"><em>OnPreDrawListener</em></a><em>_<sup><a href="https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/transition/TransitionManager.java#L250">code</a></sup>
which is invoked once all views have been properly measured and laid out, and
are ready to be drawn on screen (Step 2). In other words, when the
_OnPreDrawListener</em> is triggered, all the views involved in the
transition have their target sizes and positions in the layout. This means
it’s time to capture the end state (Step 3) for all of them<sup><a href="https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/transition/TransitionManager.java#L239">code</a></sup>—following
the same logic than the start state capturing described before.</p>
<p>With both the start and end states for all views, the transition now has enough
data to animate the views around<sup><a href="https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/transition/TransitionManager.java#L245">code</a></sup>.
It will first update or cancel any running transitions for the same
views<sup><a href="https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/transition/Transition.java#L1243">code</a></sup>
and then create new animators with the new <em>TransitionValues</em><sup><a href="https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/transition/Transition.java#L1295">code</a></sup>
(Step 4).</p>
<p>The transitions will use the start state for each view to “reset”
the UI to its original state before animating them to their end state. This is
only possible because this code runs just before the next rendering frame is
drawn i.e. inside an <em>OnPreDrawListener</em>.</p>
<p>Finally, the animators are started<sup><a href="https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/transition/Transition.java#L570">code</a></sup>
in the defined order (together or sequentially) and magic happens on screen.
**</p>
<h3 id="switching-scenes">Switching scenes</h3>
<p>The code path for switching scenes is very similar to
<em>beginDelayedTransition()</em>—the main difference being in how the layout
changes take place.</p>
<p>Calling <em>go()</em> or <em>transitionTo()</em> only differ in how they get
their transition instance. The former will just use an <em>AutoTransition</em>
and the latter will get the transition defined by the
<em>TransitionManager</em> e.g. <em>toScene</em> and <em>fromScene</em>
attributes.</p>
<p>Maybe the most relevant of aspect of scene transitions is that they effectively
replace the contents of the <em>scene root</em>. When a new scene is entered,
it will remove all views from the <em>scene root</em> and then add
itself to it<sup><a href="https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/transition/Scene.java#L160">code</a></sup>.</p>
<p>So make sure you update any class members (in your <em>Activity</em>,
<em>Fragment</em>, custom <em>View</em>, etc.) holding view references
when you switch to a new scene. You’ll also have to re-establish any dynamic
state held by the previous scene. For example, if you loaded an image from the
cloud into an <em>ImageView</em> in the previous scene, you’ll have to transfer
this state to the new scene somehow.</p>
<h3 id="some-extras">Some extras</h3>
<p>Here are some curious details in how certain transitions are implemented that
are worth mentioning.</p>
<p>The <em>ChangeBounds</em> transition is interesting in that it animates, as the
name implies, the view bounds. But how does it do this without triggering
layouts between rendering frames? It animates the view frame (left, top, right,
and bottom) which triggers size changes as you’d expect. But the view
frame is reset on every <em>layout()</em> call which could make the transition
unreliable. <em>ChangeBounds</em> avoids this problem by suppressing layout
changes on the <em>scene root</em> while the transition is running<sup><a href="https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/transition/ChangeBounds.java#L171">code</a></sup>.</p>
<p>The <em>Fade</em> transition fades views in or out according to their presence
and visibility between layout or scene changes. Among other things, it fades
out the views that have been removed from the <em>scene root</em>, which is an
interesting detail because those views will not be in the tree anymore on the
next rendering frame. <em>Fade</em> temporarily adds the removed views to the
<em>scene root</em>’s overlay to animate them out during the transition<sup><a href="https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/transition/Fade.java#L246">code</a></sup>.</p>
<h3 id="wrapping-up">Wrapping up</h3>
<p>The overall architecture of the Transitions framework is fairly simple—most of
the complexity is in the <em>Transition</em> subclasses to handle all types of
layout changes and edge cases.</p>
<p>The trick of capturing start and end states before and after an
<em>OnPreDrawListener</em> can be easily applied outside the Transitions
framework—within the limitations of not having access to certain private APIs
such as <em>ViewGroup</em>’s <em>supressLayout()</em><sup><a href="https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/view/ViewGroup.java#L5373">code</a></sup>.</p>
<p>As a quick exercise, I <a href="https://gist.github.com/lucasr/9508844">sketched</a> a
<em>LinearLayout</em> that animates layout changes using the same
technique—it’s just a quick hack, don’t use it in production! The same idea
could be applied to implement transitions in a backwards compatible way for
pre-KitKat devices, among other things.</p>
<p>That’s all for now. I hope you enjoyed it!</p>Lucas Rochalucasr@lucasr.orgOne of the biggest highlights of the Android KitKat release was the new Transitions framework which provides a very convenient API to animate UIs between different states.