Yet Another [à compléter]

Scrolling-based Effects in Android UI

Scrolling is a central behavior of a lot of common elements in Android UIs with widgets like ScrollView, ViewPager or ListView.

It also has the nice property that, knowing the scroll extents, it’s very easy to infer a floating ratio (i.e. a value between 0 and 1) from a given intermediary scroll state.

This last property makes scrolling very similar to animations, replacing the need for a time-based interpolator by this scroll-state inferred value.

In addition, since scroll events are generally triggered in response to a continuous user action, those intermediates values can be used to implement nifty navigation effects.

I have already talked about one of such effect earlier, the infamous parallax, that a lot of people are already using but there is much more opportunities around.

In the last release of my pet app Bikr for instance, I added a new statistics screen:

Stats screen

As is common in Android land, the navigation is implemented using ActionBar’s tabs at the top of the screen. This is coupled with a ViewPager instance as the root fragment layout so that screens can be accessed both by swiping and by tabs selection.

The statistics panel itself consists of a subclass of ScrollView that snaps to each of its subsection scroll offset to display multiple sub-pages.

Since both of those elements can generate scroll states, the rest of the UI take advantage of it by dynamically refreshing itself based on those value changes.

For the ViewPager for example, the tab icons are progressively made more transparent or opaque. The overall gradient background is also subtly inverted to influence the reading direction of the user upwards instead of downwards:

View Code

The scroll information of the statistics ScrollView is linked to a custom scrollbar knob that is aligned to the subsection title. I use the intermediary scroll values in that case to both infer the knob vertical position and dynamically adjust its size:

View Code: Event - Drawing

Bikr, the Fun UI Parts

A couple of days ago, I released Bikr my new Android app to effortlessly track your bicycle rides.

Right from the start the development motto was that the app should be as simple and straightforward as the problem it solves: keeping track of users rides without needing their input.

This lead to a very clean UI with no apparent settings and interaction requirements kept to a bare minimum while still making it a delightful experience.

In the rest of this post I’m going to show you some UI tidbits that got implemented in Bikr as a result.

On-boarding

View Source Code

As I mentioned in the introduction, the need for user involvement was voluntarily maintained low as the whole experience is supposed to be automatic.

As a matter of fact, the only actionable element of the entire main UI is the switch at the top-right of the screen that enable or disable globally the service.

This gave me the inspiration for this on-boarding:

Tank Fill screencast

The idea is to overlay on top of our main UI that same switch with a short explanation text and let the user initializes himself the service before being able to proceed further.

The effect is implemented by using Android view caching mechanism, retrieving the composited top-most bitmap and using Renderscript to blur the image as a background much like I described in this article.

Tank Filling Animation

View Source Code

When the app starts, each circle fills out to show you how far you are in reaching the same level than the previous day, week or month (accordingly).

When thinking about it I almost right away draw a parallel between this and filling a container with water.

This animation thus reproduce the familiar bubbling at the surface of a liquid when poured rapidly:

Tank Fill screencast

I use a sine function to distribute the bubbles uniformly across the vertical top band of the liquid and then animate them to grow out and “burst” at the surface as they get closer to the top.

Wave Simulation

View Source Code

Although the circles are not actual actionable items, when showing the app to friends they would unconsciously reach and try to interact with them.

In the spirit of rewarding the user for experimentation and not unsettling them, the water analogy is continued here by implementing a simple wave simulation that generates ripples where the user is tapping.

I also use a nice springy touch feedback that will influence the force of the final ripple to give even more touch satisfaction:

Tank Fill screencast

The simulation algorithm itself is based on the work of Matthias Müller.

Bikr, Delicious Ride Tracker

Today I’m introducing Bikr, a new Android application to track your bicycle rides.

It’s a no fuss, down to fundamental app that records how many miles you cycle and gives you a roundup per day, week and month. It will also show how you compare with the previous corresponding metrics (i.e. yesterday, previous week, previous month).

Best of all is that you don’t have to do anything. Thanks to Google Activity Recognition APIs, the app will take care of itself meaning you can just bike and forget about it.

The app is built using Xamarin as you would expect and you can find the entire source code at garuma/bikr on GitHub. As usual, code is under the Apache 2.0 license which basically mean you can reuse whatever you want.

It’s also available for download on the Play Store.

This is a joint release with James Montemagno’s MyStepCounter Android app, check it out too!

Using Parallax for Fun and Profit

The idea of a parallax is to give the illusion of depth thanks to targets seemingly moving at a different pace. You have probably already noticed the effect in real life when riding on a straight line e.g. staring at individual scenery items from a train window.

The idea is basically that the closer an object is to you, the faster it seems to move to your eyes. The extreme being the background (like distant mountains) which doesn’t seem to move at all.

This effect, called Parallax, is used proficiently on iOS and Android launchers when scrolling through your apps to make it look like your wallpaper is further away from your icons as they move.

We can re-create parallax pretty easily on Android using the ViewPager class of the support package as a foundation for handling the touch events and displaying our content.

Our goal here is to create an awesome on-boarding screen to present your app. Each “slides” will have its own background and a couple of visual elements (text, device frame, other widgets) that will be arranged in different virtual layer (i.e. some will move faster than others).

Check out an example below of such a screen and the slightly different position offsets applied to each elements:

The core idea is to setup a ViewPager taking up all the screen space (you can even use the new KitKat translucent window chrome like in the screencast with the Holo.*.NoActionBar.Translucent style) and then create a generic FrameLayout-based layout (remember how FrameLayout is your friend?) for the page. You don’t need to add an ImageView for the background since we will directly use the background property of the FrameLayout.

It’s also not necessary to have pre-scaled those background images since setting them as the background drawable of the main FrameLayout will automatically make them fit but it’s better if their dimensions are close to the form factor of the device (16/9 portrait usually) so that they don’t appear squashed (if you want to get fancy you might want to dynamically blur those too).

Afterwards, just sprinkle the layout with TextView, ImageView or whatever other view you may want to showcase your app using the layout_gravity and layout_margin attributes to align them on the screen. Here is the example, very dumb, layout I’m using for the demo:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:id="@+id/backgroundFrame"
    android:background="@drawable/onboarding_bg_1">
    <TextView
        android:text="A Big Title About Something"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/bigTitle"
        android:layout_gravity="center_horizontal|top"
        android:layout_marginTop="64dp"
        android:textColor="#ffffffff"
        android:textSize="24sp"
        android:shadowColor="#ff000000"
        android:shadowDx="0"
        android:shadowDy="1"
        android:shadowRadius="0.5" />
    <TextView
        android:text="A Subtitle About the Thing"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/bigSubtitle"
        android:layout_gravity="center_horizontal|bottom"
        android:layout_marginBottom="64dp"
        android:textColor="#ffffffff"
        android:textSize="20sp"
        android:shadowColor="#ff000000"
        android:shadowDx="0"
        android:shadowDy="1"
        android:shadowRadius="0.5" />
    <ImageView
        android:src="@android:drawable/ic_menu_gallery"
        android:layout_width="200dp"
        android:layout_height="300dp"
        android:id="@+id/centerImage"
        android:layout_gravity="center" />
</FrameLayout>

Once you have wired your ViewPager with an Adapter feeding the right content, we are going to create a PageTransformer which is where the parallax magic is gonna be created. Implementing this interface requires only one method which is called for every scroll offset of a given page View together with a value indicating its position relative to the center of the screen.

That relative position is very handy because it’s clamped between -1 (left of the screen) and +1 (right of the screen) with the zero value in the middle. That means we can derive a very simple parallax equation based on its value:

public class OnBoardingPageTransformer : Java.Lang.Object, ViewPager.IPageTransformer
{
	float parallaxCoefficient;
	float distanceCoefficient;
	IEnumerable<int>[] viewLayers;

	public OnBoardingPageTransformer (float parallaxCoefficient,
	                                  float distanceCoefficient,
	                                  params IEnumerable<int>[] viewLayers)
	{
		this.parallaxCoefficient = parallaxCoefficient;
		this.distanceCoefficient = distanceCoefficient;
		this.viewLayers = viewLayers;
	}

	public void TransformPage (View page, float position)
	{
		float coefficient = page.Width * parallaxCoefficient;
		foreach (var layer in viewLayers) {
			foreach (var viewID in layer) {
				var v = page.FindViewById (viewID);
				if (v != null)
					v.TranslationX = coefficient * position;
			}
			coefficient *= distanceCoefficient;
		}
	}
}

The trick is then to have a way to define layers and then let the user supply a couple of arguments to parametrize the effect. In the above class, we achieve this by letting the user gives a list of view IDs for each layer they want to create. You can then set the instance on your ViewPager by calling the SetPageTransformer method.

As a final piece of information, know that this technique only work on Android 3.0+ because ViewPager assumes you are using animatable properties.

Android Tip: ViewPager With Protruding Children

ViewPager is a widget part of the Support package that lets you display an horizontal list of “page”. In a nutshell, it’s to HorizontalScrollView what a ListView is to a vertical ScrollView.

By default pages are laid out to span the entire surface of the pager. In our case what we want to do is something akin to how the search results in the Apple’s AppStore iPhone app are displayed, a card-like style with adjacent items slightly taking over the surface edges:

Getting this to work with the default ViewPager class is a bit tricky. We have basically to change 3 things:

  • The global padding of the pager
  • The spacing between each page
  • The width of a single page

The first one might seem straightforward, you would just go and add both a android:paddingLeft and android:paddingRight onto the pager XML declaration. The problem is that by doing so, the clip rectangle is also moved by that same padding so it looks like the pages are cut in the middle of nowhere.

Fortunately, every ViewGroup subclass accept the android:clipToPadding attribute (doc) which allows to alter exactly that. By setting this one to false, the clip rect will remain equal to the container bounds but padding will still be taken into account during the layout phase.

The spacing between pages is a property settable on the pager itself, PageMargin (doc). This value should be about half the size of the left padding value.

Finally, to change the width of the pages, you override the GetPageWidth method of your PagerAdapter subclass (doc). The right value you use depends on the sizing you put beforehand so slightly tune it until you find the desired effect.

Applying those 3 techniques we arrive at the following result:

For reference sake, here are the XML and code used for that output:

<android.support.v4.view.ViewPager
  	android:id="@+id/pager"
  	android:gravity="center"
  	android:layout_width="match_parent"
  	android:layout_height="0px"
  	android:paddingLeft="24dp"
  	android:paddingRight="12dp"
  	android:layout_weight="1" />
var pager = view.FindViewById<ViewPager> (Resource.Id.pager);
pager.SetClipToPadding (false);
pager.PageMargin = 12.ToPixels ();
pager.Adapter = new MyPageAdapter (ChildFragmentManager);

class MyPageAdapter : FragmentStatePagerAdapter
{
	public override float GetPageWidth (int position)
	{
		return 0.93f;
	}
	
	// ...
}

FrameLayout, Your Best UI Friend

You have probably already used FrameLayout (doc) for what it’s named, adding a decoration around other content element. It can actually be much more than that and is probably one of the most versatile container of all.

The secret of FrameLayout is how it layouts its children. Although normally designed to contain one item, it will happily stack up other element on top of each other. Thus FrameLayout is essentially a way to manipulate the Z-order of views on the screen.

This is super useful for a couple of UI tricks from HUD-like elements to sliding panels to more complex animated transitions. In this post we will see an example for each of those.

Overlay Elements

As I mentioned, FrameLayout will automatically stacks its children on top of each other. This makes it really easy to implement overlay or HUD element in your interface.

The idea is to wrap the part of the UI into an outer FrameLayout instance. Generally this will be the root element of your layout. You can then add below the main layout definition the other elements you want to overlay.

These elements will generally have a fixed size or be set as wrap_content. You can then place them at the right position on screen using the layout_gravity and layout_margin XML attributes.

In the following screenshot, my main content is a Google Map view. I have then added two overlays above it, one that replicate a toast message (center-bottom corner) and a TextView displaying the last time the data was loaded (upper-right corner).

This is simply achieved with the following layout:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <!-- The main content -->
    <com.google.android.gms.maps.MapView
        android:id="@+id/map"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    <!-- "Loaded" flash bar layout -->
    <FrameLayout
        android:id="@+id/FlashBarLayout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal|bottom"
        android:layout_marginBottom="72dp">
        <!-- flash bar content -->
    </FrameLayout>
    <!-- Last loaded time layout -->
    <TextView
        android:id="@+id/UpdateTimeText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="top|right"
        android:layout_marginTop="4dp"
        android:layout_marginRight="4dp" />
</FrameLayout>

Being just normal views, you can manipulate the overlays as usual. For instance you will probably want to implement some sort of animations for transition between visibility states if the overlay is transient.

Sliding Panels

Sliding panels (aka drawers, aka fly-out menus) are the rage these days. They mainly consist of a panel that is placed, depending on the desired spatial effect, above or below another piece of content. Again this falls back to placing elements using a FrameLayout object.

The trick then is to use the translationX and translationY properties of the panel View to move the element back and forth.

Alternatively, View also exposes the OffsetTopAndBottom and OffsetLeftAndRight methods (working on display list directly) but those are not as easy to animate. Indeed, with the translation properties you can directly use ObjectAnimator or ViewPropertyAnimator.

The panel is initially “hidden” by setting its translationY property to its height (i.e. top side forced to the bottom of the screen). We create the initial appearing effect with the following code piece inside the panel class:

// As its name imply, this interpolator will let the view go slightly overboard
// and then spring it back to its place
var intp = new Android.Views.Animations.OvershootInterpolator ();
var d = Context.Resources.GetInteger (Android.Resource.Integer.ConfigMediumAnimTime);
// Only the header part of the view should be visible, we thus just make
// the bottom of the view translated off-screen
var tY = Height - FindViewById (Resource.Id.PaneHeaderView).Height;

this.Animate ().TranslationY (tY).SetDuration (d).SetInterpolator (intp).Start ();

A common idiom of those panel is also to draw a shadow on the edge where they meet the content. This can be implemented very efficiently by overriding the DispatchDraw method of the sliding panel View and drawing a black linear GradientDrawable at the right place (use the Translate method on Canvas for the right positioning).

protected override void DispatchDraw (Android.Graphics.Canvas canvas)
{
    base.DispatchDraw (canvas);
    if (shadowDrawable == null)
        shadowDrawable = new GradientDrawable (GradientDrawable.Orientation.BottomTop,
                                               new[] { Color.Argb (0x60, 0, 0, 0), Color.Argb (0, 0, 0, 0) });
    // The reserved area for the shadow is set with top padding on the view.
    shadowDrawable.SetBounds (0, 0, Width, PaddingTop);
    shadowDrawable.Draw (canvas);
}

Transitions

With the move to a Fragment world, everything becomes just another View to manage and thereby removing the need for Activity.

When you think about the different “screen” of you application as individual fragments it becomes very easy to manipulate them in fun ways. Notably, being just managed View objects, you can apply any of the animations techniques you are already familiar with to implement very nifty transition effects.

At the core of such system, you will find again the same idea of a central, expanded FrameLayout surface where your Fragment are added.

The idea is to pile the Fragment views on top of each other like a deck of card. The current screen is thus the first card of the pile and application transitions are then simply how you want the middle cards to be put on top.

I’m using this analogy of the card deck because the method we are going to use here is View.BringToFront. This method will move the view at the last position inside its parent array. The end result of that operation of course depends on the type of container but in the case of FrameLayout it will make the View be drawn on top.

Note that this method doesn’t force a relayout which is fine for FrameLayout since children individual bounds aren’t dependent on each other but in the case of e.g. LinearLayout you will have to call RequestLayout for the modification to happen.

We will assume the layout of our main Activity is a single full-size FrameLayout container. Our application is composed of two screens represented as two fragments.

Initially, we create and attach directly those fragments to our content frame, hiding the second one to get into an initial state:

FirstFragment fragment1;
SecondFragment fragment2;

Fragment currentFragment;

protected override void OnCreate (Bundle bundle)
{
	SetContentView (/* ... */);
	
	fragment1 = new FirstFragment (this);
	fragment2 = new SecondFragment (this);
	
	SupportFragmentManager.BeginTransation ()
		.Add (Resource.Id.content_frame, fragment2, SecondFragment.Name)
		.Hide (fragment2)
		.Add (Resource.Id.content_frame, fragment1, FirstFragment.Name)
		.Commit ();
	currentFragment = fragment1;
}

When we want to change the Fragment that is shown, we will make sure that both views are on top of the drawing pile with the current one being on top of the other using BringToFront. We then swap the two fragments in a FragmentTransaction:

void SwitchTo (Fragment fragment, string name)
{
	if (fragment.IsVisible)
		return;
	var t = SupportFragmentManager.BeginTransaction ();
	t.SetCustomAnimations (Resource.Animator.frag_slide_in,
	                       Resource.Animator.frag_slide_out);

	// Make sure the next view is below the current one
	fragment.View.BringToFront ();
	// And bring the current one to the very top
	currentFragment.View.BringToFront ();

	// Hide the current fragment
	t.Hide (currentFragment);
	t.Show (existingFragment);
	currentFragment = existingFragment;

	// You probably want to add the transaction to the backstack
	// so that user can use the back button
	t.AddToBackStack (null);
	t.Commit ();
}

To add a bit of woosh, we are using customs animations to mark the transition between the two fragments to get this result:

These animations need to be defined in an XML resource using either property animator (aka from Android.Animations) if you are using framework Fragment or using view animations (aka from Android.View.Animations) if you are using Fragment from the support package.

To register custom animations, we call the SetCustomAnimations method on FragmentTransaction like so:

t.SetCustomAnimations (Resource.Animator.frag_slide_in,
                       Resource.Animator.frag_slide_out);

The first animation will be run on any fragment added/attached/shown while the second animation will be played on fragments hidden/detached/removed. Both are ran at the same time but you can use the animation startOffset attribute to delay one or the other.

For instance in my example, my frag_slide_out animation is defined like so:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:interpolator="@android:interpolator/accelerate_cubic">
   <translate
      android:fromXDelta="0"
      android:toXDelta="100%"
      android:duration="300" />
   <alpha
        android:fromAlpha="1.0"
        android:toAlpha="0.3"
        android:duration="300" />
</set>

Conclusion

Everything I have presented here is part of a big refresh to Moyeu. You can view the full source code on the GitHub repository at garuma/Moyeu.

XamSvg, Managed SVG Support for Android

As part of the new release of Moyeu, I’m also finally publishing a library that I have been using for some time.

This library is called XamSvg and in a nutshell it’s a managed (partial) implementation of an SVG parser/renderer for, currently only, Xamarin.Android.

The idea stems from the fact that Android has by default no serious way to do scalable graphics in a world with an exploding amount of device densities.

SVG appeared as a good fit because it’s easy to parse (XML), text-based (compress well) and fit well with the Android graphics API. It’s also incidentally the format used by the lovely Inkscape which makes my work even easier.

Before anyone gets their hopes high and starts throwing fancy SVG tigers at the library, know that this is by no mean meant to be a complete implementation of the standard.

As described in the README, the goal is to provide a minimum viable product™ to support traditional shape-based icons.

Still, I haven’t had any problem parsing all the SVG that I have created as almost all the icons in Moyeu are entirely SVG based. In general, if you follow the exporting guide in the README you should be fine.

The other good thing about the library is that it’s very simple to use. You will generally use only one class, SvgFactory and its two methods GetBitmap / GetDrawable:

var bmp = SvgFactory.GetBitmap (Activity.Resources, Resource.Raw.ic_svg, 48, 48);
var drawable = SvgFactory.GetDrawable (Activity.Resources, Resource.Raw.ic_svg);

I recommend the second form as the returned drawable will automatically scale depending on the bounds requested. This means that if you have an ImageView set up using dp units like this:

<ImageView
    android:layout_width="14dp"
    android:layout_height="9dp"
    android:scaleType="fitXY" />

The displayed image will look the same crispy crispness on every devices no matter how many pixel per inches they have.

Another cool thing about the library is that it lets you dynamically adapt colors at runtime. To do so, you need to provide an implementation of the ISvgColorMapper interface which sports the single method Color MapColor (Color c) (instance that can also be retrieved from a Func<Color, Color>).

I use this tricks for the pins that are displayed by Moyeu on the map. Concretely, the pins are all the same base SVG:

On which I can apply tinting with my color mapper:

Color ColorReplacer (Color inColor, float ratio)
{
	Color tone = InterpolateColor (baseLightRedColor, baseLightGreenColor, ratio);
	Color result = inColor;

	if (inColor == fillColor)
		result = tone;
	else if (inColor == borderColor)
		result = Lighten (tone, 0x20);
	else if (inColor == bottomFillColor)
		result = InterpolateColor (baseDarkRedColor, baseDarkGreenColor, ratio);

	return result;
}

In order to have a multi-colored view of the world:

You can find more usage sample of the library in Moyeu source code.

A Big Refresh to Moyeu

Moyeu is an Android application that hopes to provide a better alternative to Boston’s Hubway bike sharing system. You can view the introductory post I made at the time.

This new version sports a good number of new features and general UI refresh.

For starter, the navigation is now exposed as a drawer that also lets you visualize stations that are near you.

Instead of the old info window style, information about a station are presented with a sliding info view in the same fashion than the new Google Maps application. That info view when expanded also shows you a Google Street View shot of the station surroundings for easier pinpointing.

The pins displayed on the map are also now showing the number of bikes available at each station. They also have a better tinting system to provide a direct feedback on each station bike availability.

Just for fun, I also made a little featurette page for the app. Check it out at moyeuapp.net.

For installation, head to the usual corner of the web:

Get it on Google Play