Category Archives: Animation

Creating a Google Fit style circular animated view

Google’s health tracking platform, Google Fit, provides data visualization using a circular view to display feedback to users. This circular view is smoothly animated and presents a variety of data about the current status of the users fitness goals.

This article takes you through the steps required to build the chart shown in the clip below and add it into your Android App.

DecoView

DecoView was designed as a fully configurable library for animating arc based charts. Continue reading this article to find out how to reproduce the Google Fit user experience in your app with DecoView. DecoView is open source and available on GitHub.

DecoView is subclassed from the Android View class. Just like other View subclasses, such as TextView and ImageView, it can be added and configured from your layout XML then controlled in your Activity code.

Faux Fit: DecoView sample project

The source code is available from the Faux Fit project on GitHub.

Step 1: Include the DecoView library in your project

DecoView is hosted on the JitPack.io repository. The first step is to add the required repository into your build.gradle file

repositories {
    // ...
    maven { url "https://jitpack.io" }
}

Next, add the gradle dependency for DecoView

dependencies {
    compile 'com.github.bmarrdev:android-DecoView-charting:v0.9.3'
}

Step 2: Add a DecoView to your XML layout

Open your Activity layout file and add a DecoView by inserting the following declaration.

<com.hookedonplay.decoviewlib.DecoView
    xmlns:custom="http://schemas.android.com/apk/res-auto"
    custom:dv_lineWidth="26dp"
    android:id="@+id/dynamicArcView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

Note the use of the custom:dv_lineWidth attribute to set the default width of the arcs. You can always override this value for any of the data series you add later.

Step 3: Configure the DecoView by adding a data series

After adding a DecoView to your XML layout you will not see anything in your app unless you add one or more data series.

The first data series we are adding is going to be used as a background track to show the path where the other data series will follow as they are animated. To add this background arc add the following code to your Activity source file. Note how we set the color to a very light gray (hex code #FFE2E2E2).

DecoView decoView = (DecoView) findViewById(R.id.dynamicArcView);

SeriesItem seriesItem = new SeriesItem.Builder(Color.parseColor("#FFE2E2E2"))
    .setRange(0, 50, 0)
    .build();

int backIndex = decoView.addSeries(seriesItem);

Tip: While we are setting the color to be fully opaque here, DecoView supports alpha transparency in all functions that take a Color value

Next you need to add an actual data series in your Activity code. This may be used to visualize a data value such as the number of calories the user has burnt or number of minutes spent walking.

final SeriesItem seriesItem = new SeriesItem.Builder(Color.parseColor("#FFFF8800"))
        .setRange(0, 50, 0)
        .build();

int series1Index = decoView.addSeries(seriesItem);

Step 4: Add a listener to monitor value changes

First you need to add a TextView to your XML layout. Then you can attach a new SeriesItem.SeriesItemListener() that will be called whenever the current position of the data series is changed. If you issue an event to move the series this callback will be called multiple times for the one event.

We will calculate the percentage the data series is filled and update our TextView with this percentage value.

final TextView textPercentage = (TextView) findViewById(R.id.textPercentage);
seriesItem.addArcSeriesItemListener(new SeriesItem.SeriesItemListener() {
    @Override
    public void onSeriesItemAnimationProgress(float percentComplete, float currentPosition) {
        float percentFilled = ((currentPosition - seriesItem.getMinValue()) / (seriesItem.getMaxValue() - seriesItem.getMinValue()));
        textPercentage.setText(String.format("%.0f%%", percentFilled * 100f));
    }

    @Override
    public void onSeriesItemDisplayProgress(float percentComplete) {

    }
});

Tip: When implementing the DecoView progress listeners be aware that they will be called many times for each event. Avoid heavy processing in the listener to prevent jank, and avoid new allocations to prevent memory churn.

Step 5: Create timed events to animate the DecoView data series

We need to set the background track to be at the maximum value, this enables it to be drawn as a complete circle. We could have set this as the initial position for the track when it was constructed, however we want to see an animation filling the background track so we use an event to reveal the background track. While it is possible to fully customize this animation we will keep it simple and just set the minimum information required; that is the position to move the value and the index of the data series to execute the event.

decoView.addEvent(new DecoEvent.Builder(50)
        .setIndex(backIndex)
        .build());

It is possible to animate the DecoView at any time in response to a user action, such as pressing a button or using a swipe gesture. This is what we have just created with an event to be executed immediately.

It is also possible to delay events to happen at a future time. We will construct then event using setDelay(5000) to delay the animation of the main data series. We need to set the current value between the minimum (0) and maximum (50) values specified on creation of the data series.

Here is how to move the current position to 16.3f (out of 50) and delay the event to start after 5 seconds. After 10 seconds we will then move the position to 30.

decoView.addEvent(new DecoEvent.Builder(16.3f)
        .setIndex(series1Index)
        .setDelay(5000)
        .build());

decoView.addEvent(new DecoEvent.Builder(30f)
        .setIndex(series1Index)
        .setDelay(10000)
        .build());

Note: All time values in DecoView are specified in Milliseconds, 5000ms == 5 seconds

Step 6. Run your app to test your DecoView

You should now have the DecoView animating and looking similar to the image below when you run your app.

DecoView basic sample

Step 7. Add more data series, custom reveal animations and event listeners

To finish off building the Faux Fit sample you need to:

  • Add two more data series. This can be done using the same method used to create the existing series
  • Add custom reveal and hide effects
  • Create a animation loop by adding a listener to recreate all the events when the last one has completed

The code required to complete these final tasks can be viewed in the Faux Fit Activity found within the GitHub project. You should end up with an Activity something like the snapshot below.

Faux Fit DecoView Sample

License

Copyright 2015 Brent Marriott

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

References

DecoView on GitHub: https://github.com/bmarrdev/android-DecoView-charting

Faux Fit sample on GitHub: https://github.com/bmarrdev/fauxfit-decoview-sample

Advertisements

3 Comments

Filed under Android Libraries, Android Studio, Animation, Charting

Animating numerical change using Android Interpolators

Count von Count kneeling

I recently completed a financial charting tool with several user controlled variables, such as interest rate, term, loan amount. One client request was on the reset to default values there should be an animation while incrementing/decrementing rather than the value jumping instantly. I decided a non-linear animation algorithm would give the best visual result by adjusting the values quickly before decelerating as the target value approached. The graph below shows the required deceleration over time.

Decelerate graph

You may recognize this as a common interpolation pattern used in Android for animating movement of views. With this in mind I decided the most flexible design was to utilize the built-in Android Interpolator classes to control the rate of increment/decrement while using ValueAnimator. I have packaged the end result into an easy to use class, AnimateCounter.

AnimateCounter

AnimateCounter is a simple to use class to animate a numerical count on any TextView derived object. Full source code is available here.

The animation below shows an example of AnimateCounter being used to animate four TextViews from 0 to 100. Notice how the rate of numerical change for each view is influenced by the use of the different Interpolators.

Android Interpolation examples

Features of AnimateCounter

  • Ascending or descending value animation
  • Supports standard Android Interpolator classes
  • Supports custom Interpolator classes
  • Decimal and floating point number support
  • Numerical precision support
  • Optional callback on completion

AnimateCounter Usage

An AnimateCounter object is constructed using the builder design pattern. At a minimum a TextView is required for construction, then use the builder functions to optionally set initial and final values, precision, total duration and the interpolator required for animation. If no interpolator is provided the ValueAnimator default, AccelerateDecelerateInterpolator, is used.

Example 1: Count from 0 to 100 in two seconds using the default AccelerateDecelerateInterpolator interpolation.

AnimateCounter animateCounter = new AnimateCounter.Builder(textView)
        .setCount(0, 100)
        .setDuration(2000)
        .build();

animateCounter.execute();

Example 2: Count from 0.00 to 1.00 (floating point number with 2 decimal places) in 10 seconds using LinearInterpolator.

AnimateCounter animateCounter = new AnimateCounter.Builder(textView)
        .setCount(0f, 1f, 2)
        .setDuration(10000)
        .setInterpolator(new LinearInterpolator())
        .build();

animateCounter.execute();

Example 3: Perform an action after counting from 10 to 0 using the OvershootInterpolator.

AnimateCounter animateCounter = new AnimateCounter.Builder(textView)
        .setCount(10, 0)
        .setDuration(2000)
        .setInterpolator(new OvershootInterpolator())
        .build();

animateCounter.setAnimateCounterListener(new AnimateCounter.AnimateCounterListener() {
    @Override
    public void onAnimateCounterEnd() {
        // Add action here!
    }
});

animateCounter.execute();

Built-in Interpolators

Here are some of the Interpolators that you may like to use to animate the numerical change. These are all provided as part of the Android SDK:

Roll your own: Custom Interpolators

If none of the built in Interpolators fit your needs it is simple to create your own. The following class shows how to create an acceleration deceleration interpolation result. You could replace the mathematical formula in this class as required to produce your desired result.

import android.view.animation.Interpolator;
public class CustomAccelerateDecelerateInterpolator implements Interpolator {
 /**
  * Maps a value representing the elapsed fraction of an animation to a value that represents
  * the interpolated fraction. This interpolated value is then multiplied by the change in value
  * of an animation to derive the animated value at the current elapsed animation time.
  *
  * @param input A value between 0 and 1.0 indicating our current point in the animation
  *              where 0 represents the start and 1.0 represents the end
  *              
  * @return The interpolation value. This value can be more than 1.0 for interpolators 
  * which overshoot their targets, or less than 0 for interpolators that undershoot their targets. 
  */
  public float getInterpolation(float input) {
    return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
  } 
} 

Graphing tools can be used to visualize the interpolation algorithms and are the best place to start if you want to build our own custom interpolator. Here is an example.

Step 1. Find a mathematical equation for interpolation. The following equation shows a formula used in the Android accelerate/decelerate interpolator, where time is represented by t.

aceleration deceleration function

Step 2. Graph your equation. Visit graphsketch.com or any graphing site and enter in the formula. When using graphsketch.com it is required in this format:

cos((x+1)pi)/2)+0.5

The generated graph should confirm if the output from the formula is going to meet your needs. Using the above example the following graph is produced. Note the acceleration during the first half of the duration and the deceleration during the final half.

Accelerate decelerate graph

Step 3. Convert into Java. To use in your custom interpolator you need to convert your equation into Java code, for example:

cos((x+1)pi)/2)+0.5
(float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;

Source Code

Source for AnimateCounter available on GitHub

Attribution

“Count von Count kneeling” by Source. Licensed under Fair use via Wikipedia – https://en.wikipedia.org/wiki/File:Count_von_Count_kneeling.png#/media/File:Count_von_Count_kneeling.png

1 Comment

Filed under Android SDK, Animation