Graphics and Animation

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 14

Graphics and Animation

Android provides a very rich and diverse framework for supporting 2D graphics and animations.

Despite running on devices that are traditionally of limited power, the highest rated mobile
applications often have a sophisticated User Experience (UX), complete with high quality
graphics and animations that provide an intuitive, responsive, dynamic feel. As mobile
applications get more and more sophisticated, users have begun to expect more and more from
applications.

Luckily for us, modern mobile platforms have very powerful frameworks for creating
sophisticated animations and custom graphics while retaining ease of use. This enables
developers to add rich interactivity with very little effort.

UI API frameworks in Android can roughly be split into two categories: Graphics and
Animation.

Graphics are further split into different approaches for doing 2D and 3D graphics. 3D graphics
are available via a number of built in frameworks such as OpenGL ES (a mobile specific version
of OpenGL), and third-party frameworks such as MonoGame (a cross platform toolkit
compatible with the XNA toolkit). Android provides two different API's for creating 2D
graphics. One is a high level declarative approach and the other a programmatic low-level API:

 Drawable Resources – These are used to create custom graphics either


programmatically or (more typically) by embedding drawing instructions in XML files.
Drawable resources are typically defined as XML files that contain instructions or actions
for Android to render a 2D graphic.
 Canvas – this is a low level API that involves drawing directly on an underlying bitmap.
It provides very fine-grained control over what is displayed.

In addition to these 2D graphics techniques, Android also provides several different ways to
create animations:

 Drawable Animations – Android also supports frame-by-frame animations known as


Drawable Animation. This is the simplest animation API. Android sequentially loads and
displays Drawable resources in sequence (much like a cartoon).
 View Animations – View Animations are the original animation API's in Android and are
available in all versions of Android. This API is limited in that it will only work with
View objects and can only perform simple transformations on those Views. View
animations are typically defined in XML files found in the /Resources/anim folder.
 Property Animations – Android 3.0 introduced a new set of animation API's known as
Property Animations. These new API's introduced an extensible and flexible system that
can be used to animate the properties of any object, not just View objects. This flexibility
allows animations to be encapsulated in distinct classes that will make code sharing
easier.
View Animations are more suitable for applications that must support the older pre-Android 3.0
API's (API level 11). Otherwise applications should use the newer Property Animation API's for
the reasons that were mentioned above.

All of these frameworks are viable options, however where possible, preference should be given
to Property Animations, as it is a more flexible API to work with. Property Animations allow for
animation logic to be encapsulated in distinct classes that makes code sharing easier and
simplifies code maintenance.

Accessibility

Graphics and animations help to make Android apps attractive and fun to use; however, it is
important to remember that some interactions occur via screenreaders, alternate input devices, or
wi th assisted zoom. Also, some interactions may occur without audio capabilities.

Apps are more usable in these situations if they have been designed with accessibility in mind:
providing hints and navigation assistance in the user-interface, and ensuring there is text-content
or descriptions for pictorial elements of the UI.

2D Graphics

Drawable Resources are a popular technique in Android applications. As with other resources,
Drawable Resources are declarative – they're defined in XML files. This approach allows for a
clean separation of code from resources. This can simplify development and maintenance
because it is not necessary to change code to update or change the graphics in an Android
application. However, while Drawable Resources are useful for many simple and common
graphic requirements, they lack the power and control of the Canvas API.

The other technique, using the Canvas object, is very similar to other traditional API frameworks
such as System.Drawing or iOS's Core Drawing. Using the Canvas object provides the most
control of how 2D graphics are created. It is appropriate for situations where a Drawable
Resource will not work or will be difficult to work with. For example, it may be necessary to
draw a custom slider control whose appearance will change based on calculations related to the
value of the slider.

Drawable Resources
Drawable Resources are defined in an XML file in the directory /Resources/drawable. Unlike
embedding PNG or JPEG's, it is not necessary to provide density-specific versions of Drawable
Resources. At runtime, an Android application will load these resources and use the instructions
contained in these XML files to create 2D graphics. Android defines several different types of
Drawable Resources:

Drawable Resource Description


ShapeDrawable This is a Drawable object that draws a primitive geometric shape and
Drawable Resource Description
applies a limited set of graphical effects on that shape. They are very useful
for things such as customizing Buttons or setting the background of
TextViews.
This is a Drawable Resource that will change appearance based on the state
StateListDrawable of a widget/control. For example, a button may change its appearance
depending on whether it is pressed or not.
This Drawable Resource that will stack several other drawables one on top
of another. An example of a LayerDrawable is shown in the following
screenshot:
LayerDrawable

This is a LayerDrawable but with one difference. A TransitionDrawable is


TransitionDrawable
able to animate one layer showing up over top another.
This is very similar to a StateListDrawable in that it will display an image
based on certain conditions. However, unlike a StateListDrawable, the
LevelListDrawable displays an image based on an integer value. An
LevelListDrawable
example of a LevelListDrawable would be to display the strength of a WiFi
signal. As the strength of the WiFi signal changes, the drawable that is
displayed will change accordingly.
As their name implies, these Drawables provide both scaling and clipping
ScaleDrawable /
functionality. The ScaleDrawable will scale another Drawable, while the
ClipDrawable
ClipDrawable will clip another Drawable.
This Drawable will apply insets on the sides of another Drawable resource.
InsetDrawable It is used when a View needs a background that is smaller than the View's
actual bounds.
This file is a set of instructions, in XML, that are to be performed on an
XML actual bitmap. Some actions that Android can perform are tiling, dithering,
BitmapDrawable and anti-aliasing. One of the very common uses of this is to tile a bitmap
across the background of a layout.

Drawable Example

How to create a 2D graphic using a ShapeDrawable. A ShapeDrawable can define one of the
four basic shapes: rectangle, oval, line, and ring. It is also possible to apply basic effects, such as
gradient, colour, and size. The following XML is a ShapeDrawable that may be found in the
AnimationsDemo companion project (in the file
Resources/drawable/shape_rounded_blue_rect.xml). It defines a rectangle with a purple
gradient background and rounded corners:

<?xml version="1.0" encoding="utf-8"?>


<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!-- Specify a gradient for the background -->
<gradient android:angle="45"
android:startColor="#55000066"
android:centerColor="#00000000"
android:endColor="#00000000"
android:centerX="0.75" />

<padding android:left="5dp"
android:right="5dp"
android:top="5dp"
android:bottom="5dp" />

<corners android:topLeftRadius="10dp"
android:topRightRadius="10dp"
android:bottomLeftRadius="10dp"
android:bottomRightRadius="10dp" />
</shape>

We can reference this Drawable Resource declaratively in a Layout or other Drawable as shown
in the following XML:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#33000000">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="@drawable/shape_rounded_blue_rect"
android:text="@string/message_shapedrawable" />
</RelativeLayout>

Drawable Resources can also be applied programmatically. The following code snippet shows
how to programmatically set the background of a TextView:

TextView tv = FindViewById<TextView>(Resource.Id.shapeDrawableTextView);
tv.SetBackgroundResource(Resource.Drawable.shape_rounded_blue_rect);

Using the Canvas Drawing API

Drawables are powerful but have their limitations. Certain things are either not possible or very
complex (for example: applying a filter to a picture that was taken by a camera on the device). It
would be very difficult to apply red-eye reduction by using a Drawable Resource. Instead, the
Canvas API allows an application to have very fine-grained control to selectively change colors
in a specific part of the picture.

One class that is commonly used with the Canvas is the Paint class. This class holds colour and
style information about how to draw. It is used to provide things such a color and transparency.
The Canvas API uses the painter's model to draw 2D graphics. Operations are applied in
successive layers on top of each other. Each operation will cover some area of the underlying
bitmap. When the area overlaps a previously painted area, the new paint will partially or
completely obscure the old. This is the same way that many other drawing APIs such as
System.Drawing and iOS's Core Graphics work.

There are two ways to obtain a Canvas object. The first way involves defining a Bitmap object,
and then instantiating a Canvas object with it. For example, the following code snippet creates a
new canvas with an underlying bitmap:

Bitmap bitmap = Bitmap.CreateBitmap(100, 100, Bitmap.Config.Argb8888);


Canvas canvas = new Canvas(b);

The other way to obtain a Canvas object is by the OnDraw callback method that is provided the
View base class. Android calls this method when it decides a View needs to draw itself and
passes in a Canvas object for the View to work with.

The Canvas class exposes methods to programmatically provide the draw instructions. For
example:

 Canvas.DrawPaint(Paint p) – Fills the entire canvas's bitmap with the specified paint.
 Canvas.DrawPath(Path path, Paint paint) – Draws the specified geometric shape
using the specified paint.
 Canvas.DrawText(String text, float x, float y, Paint paint) – Draws the
text on the canvas with the specified colour. The text is drawn at location x,y .

Drawing with the Canvas API


public class MyView : View
{
protected override void OnDraw(Canvas canvas)
{
base.OnDraw(canvas);
Paint green = new Paint {
AntiAlias = true,
Color = Color.Rgb(0x99, 0xcc, 0),
};
green.SetStyle(Paint.Style.FillAndStroke);
Paint red = new Paint {
AntiAlias = true,
Color = Color.Rgb(0xff, 0x44, 0x44)
};
red.SetStyle(Paint.Style.FillAndStroke);
float middle = canvas.Width * 0.25f;
canvas.DrawPaint(red);
canvas.DrawRect(0, 0, middle, canvas.Height, green);
}}
This code above first creates a red paint and a green paint object. It fills the content of the canvas
with red, and then instructs the canvas to draw a green rectangle that is 25% of the width of the
canvas. An example of this can be seen by in AnimationsDemo project that is included with the
source code for this article. By starting up the application and selecting the Drawing item from
the main menu, we should a screen similar to the following:

Animation
Users like things that move in their applications. Animations are a great way to improve the user
experience of an application and help it stand out. The best animations are the ones that users
don't notice because they feel natural. Android provides the following three API's for animations:

 View Animation – This is the original API. These animations are tied to a specific View
and can perform simple transformations on the contents of the View. Because of it's
simplicity, this API still useful for things like alpha animations, rotations, and so forth.
 Property Animation – Property animations were introduced in Android 3.0. They enable
an application to animate almost anything. Property animations can be used to change
any property of any object, even if that object is not visible on the screen.
 Drawable Animation – This a special Drawable resource that is used to apply a very
simple animation effect to layouts.

In general, property animation is the preferred system to use as it is more flexible and offers
more features.

View Animations
View animations are limited to Views and can only perform animations on values such as start
and end points, size, rotation, and transparency. These types of animations are typically referred
to as tween animations. View animations can be defined two ways – programmatically in code or
by using XML files. XML files are the preferred way to declare view animations, as they are
more readable and easier to maintain.
The animation XML files will be stored in the /Resources/anim directory of a
Xamarin.Android project. This file must have one of the following elements as the root element :

 alpha – A fade-in or fade-out animation.


 rotate – A rotation animation.
 scale – A resizing animation.
 translate – A horizontal and/or vertical motion.
 set – A container that may hold one or more of the other animation elements.

By default, all animations in an XML file will be applied simultaneously. To make animations
occur sequentially, set the android:startOffset attribute on one of the elements defined
above.
It is possible to affect the rate of change in an animation by using an interpolator. An
interpolator makes it possible for animation effects to be accelerated, repeated, or decelerated.
The Android framework provides several interpolators out of the box, such as (but not limited
to):

 AccelerateInterpolator / DecelerateInterpolator – these interpolators increase or


decrease the rate of change in an animation.
 BounceInterpolator – the change bounces at the end.
 LinearInterpolator – the rate of changes is constant.

The following XML shows an example of an animation file that combines some of these
elements:

<?xml version="1.0" encoding="utf-8"?>


<set xmlns:android=http://schemas.android.com/apk/res/android
android:shareInterpolator="false">

<scale
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:fromXScale="1.0"
android:toXScale="1.4"
android:fromYScale="1.0"
android:toYScale="0.6"
android:pivotX="50%"
android:pivotY="50%"
android:fillEnabled="true"
android:fillAfter="false"
android:duration="700" />

<set android:interpolator="@android:anim/accelerate_interpolator">
<scale android:fromXScale="1.4"
android:toXScale="0.0"
android:fromYScale="0.6"
android:toYScale="0.0"
android:pivotX="50%"
android:pivotY="50%"
android:fillEnabled="true"
android:fillBefore="false"
android:fillAfter="true"
android:startOffset="700"
android:duration="400" />

<rotate android:fromDegrees="0"
android:toDegrees="-45"
android:toYScale="0.0"
android:pivotX="50%"
android:pivotY="50%"
android:fillEnabled="true"
android:fillBefore="false"
android:fillAfter="true"
android:startOffset="700"
android:duration="400" />
</set>
</set>

This animation will perform all of the animations simultaneously. The first scale animation will
stretch the image horizontally and shrink it vertically, and then the image will simultaneously be
rotated 45 degrees counter-clockwise and shrink, disappearing from the screen.

The animation can be programmatically applied to a View by inflating the animation and then
applying it to a View. Android provides the helper class
Android.Views.Animations.AnimationUtils that will inflate an animation resource and
return an instance of Android.Views.Animations.Animation. This object is applied to a View
by calling StartAnimation and passing the Animation object. The following code snippet
shows an example of this:

Animation myAnimation =
AnimationUtils.LoadAnimation(Resource.Animation.MyAnimation);
ImageView myImage = FindViewById<ImageView>(Resource.Id.imageView1);
myImage.StartAnimation(myAnimation);

Now that we have a fundamental understanding of how View Animations work, lets move to
Property Animations.

Property Animations

Property animators are a new API that was introduced in Android 3.0. They provide a more
extensible API that can be used to animate any property on any object.

All property animations are created by instances of the Animator subclass. Applications do not
directly use this class, instead they use one of it's subclasses:

 ValueAnimator – This class is the most important class in the entire property animation
API. It calculates the values of properties that need to be changed. The ViewAnimator
does not directly update those values; instead, it raises events that can be used to update
animated objects.
 ObjectAnimator – This class is a subclass of ValueAnimator . It is meant to simplify
the process of animating objects by accepting a target object and property to update.
 AnimationSet – This class is responsible for orchestrating how animations run in
relation to one another. Animations may run simultaneously, sequentially, or with a
specified delay between them.

Evaluators are special classes that are used by animators to calculate the new values during an
animation. Out of the box, Android provides the following evaluators:

 IntEvaluator – Calculates values for integer properties.


 FloatEvaluator – Calculates values for float properties.
 ArgbEvaluator – Calculates values for colour properties.

If the property that is being animated is not a float, int or colour, applications may create their
own evaluator by implementing the ITypeEvaluator interface. (Implementing custom
evaluators is beyond the scope of this document.)

Using the ValueAnimator

There are two parts to any animation: calculating animated values and then setting those values
on properties on some object. ValueAnimator will only calculate the values, but it will not
operate on objects directly. Instead, objects will be updated inside event handlers that will be
invoked during the animation lifespan. This design allows several properties to be updated from
one animated value.

You obtain an instance of ValueAnimator by calling one of the following factory methods:

 ValueAnimator.OfInt
 ValueAnimator.OfFloat
 ValueAnimator.OfObject

Once that is done, the ValueAnimator instance must have its duration set, and then it can be
started. The following example shows how to animate a value from 0 to 1 over the span of 1000
milliseconds:

ValueAnimator animator = ValueAnimator.OfInt(0, 100);


animator.SetDuration(1000);
animator.Start();

But itself, the code snippet above is not very useful – the animator will run but there is no target
for the updated value. The Animator class will raise the Update event when it decides that it is
necessary to inform listeners of a new value. Applications may provide an event handler to
respond to this event as shown in the following code snippet:

MyCustomObject myObj = new MyCustomObject();


myObj.SomeIntegerValue = -1;

animator.Update += (object sender, ValueAnimator.AnimatorUpdateEventArgs e)


=>
{
int newValue = (int) e.Animation.AnimatedValue;
// Apply this new value to the object being animated.
myObj.SomeIntegerValue = newValue;
};

Now that we have an understanding of ValueAnimator, lets learn more about the
ObjectAnimator.

Using the ObjectAnimator

ObjectAnimator is a subclass of ViewAnimator that combines the timing engine and value
computation of the ValueAnimator with the logic required to wire up event handlers. The
ValueAnimator requires applications to explicitly wire up an event handler – ObjectAnimator
will take care of this step for us.

The API for ObjectAnimator is very similar to the API for ViewAnimator, but requires that you
provide the object and the name of the property to update. The following example shows an
example of using ObjectAnimator:

MyCustomObject myObj = new MyCustomObject();


myObj.SomeIntegerValue = -1;

ObjectAnimator animator = ObjectAnimator.OfFloat(myObj, "SomeIntegerValue",


0, 100);
animator.SetDuration(1000);
animator.Start();

As you can see from the previous code snippet, ObjectAnimator can reduce and simplify the
code that is necessary to animate an object.

Drawable Animations
The final animation API is the Drawable Animation API. Drawable animations load a series of
Drawable resources one after the other and display them sequentially, similar to a flip-it cartoon.

Drawable resources are defined in an XML file that has an <animation-list> element as the
root element and a series of <item> elements that define each frame in the animation. This XML
file is stored in the /Resource/drawable folder of the application. The following XML is an
example of a drawable animation:

<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/asteroid01" android:duration="100" />
<item android:drawable="@drawable/asteroid02" android:duration="100" />
<item android:drawable="@drawable/asteroid03" android:duration="100" />
<item android:drawable="@drawable/asteroid04" android:duration="100" />
<item android:drawable="@drawable/asteroid05" android:duration="100" />
<item android:drawable="@drawable/asteroid06" android:duration="100" />
</animation-list>
This animation will run through six frames. The android:duration attribute declares how long
each frame will be displayed. The next code snippet shows an example of creating a Drawable
animation and starting it when the user clicks a button on the screen:

AnimationDrawable _asteroidDrawable;

protected override void OnCreate(Bundle bundle)


{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);

_asteroidDrawable = (Android.Graphics.Drawables.AnimationDrawable)
Resources.GetDrawable(Resource.Drawable.spinning_asteroid);

ImageView asteroidImage =
FindViewById<ImageView>(Resource.Id.imageView2);
asteroidImage.SetImageDrawable((Android.Graphics.Drawables.Drawable)
_asteroidDrawable);

Button asteroidButton = FindViewById<Button>(Resource.Id.spinAsteroid);


asteroidButton.Click += (sender, e) =>
{
_asteroidDrawable.Start();
};
}

At this point we have covered the foundations of the animation APIs available in an Android
application.

Custom Views
Default views
The Android framework provides several default views. The base class a view is the View.
Views are responsible for measuring, layouting and drawing themselves and their child elements
(in case of a ViewGroup). Views are also responsible for saving their UI state and handling touch
events. Developers can also create custom views and use them in their application.
It is possible to create custom views by:
 Compound views - combining views with a default wiring
 Custom views - creating your own views
o by extending an existing view, e.g. Button
o by extending the View class
Compound Views
Compound views (also known as Compound Components ) are pre-configured ViewGroups
based on existing views with some predefined view interaction.
Combound views also allow you to add custom API to update and query the state of the
combound view.
For such a control you define a layout file and assign it to your compound view. In the
implementation of your compound view you predefine the view interaction. You would define a
layout file and extend the corresponding ViewGroup class. In this class you inflate the layout file
and implement the View connection logic
For performance reasons you may want to rewrite your combound view to a custom view which
extends View. This may you can typically flatten your view hierarchy. Drawing the view
requires in this case less traversals and this can be significantly faster if implemented correctly.

Creating custom views


Creating custom views
By extending the View class or one of its subclasses you can create your custom view.
For drawing view use the onDraw() method. In this method you receive a Canvas object which
allows you to perform drawing operations on it, e.g. draw lines, circle, text or bitmaps. If the
view should be re-drawn you call the invalidate() method which triggers a call to the
onDraw() method of this view.
If you define own views, ensure you review the ViewConfiguration class, as it contains
several constants for defining views.
To draw your Views you typically use the 2D Canvas API.

Measurement
The layout manager calls the onMeasure() method of the view. The view receives the layout
parameter from the layout manager. A layout manager is responsible to determine the size of all
its children.
The view must call the setMeasuredDimenstion(int, int) method with the result.

Defining custom layout managers


You can implement your custom layout manager by extending the ViewGroup class. This allows
you to implement more efficient layout managers or to implement effects which are currently
missing in the Android platform.
A custom layout manager can override the onMeasure() and onLayout() method and specialize
the calculation of its children. For example it can leave out the time consuming support of
layout_weight of the LinearLayout class.
To calculate the size of the child you can use the measureChildWithMargins() method of the
ViewGroup class.
It is good practice to store any additional layout parameters in an inner class of your ViewGroup
implementation. For example ViewGroup.LayoutParams ` implements command layout
parameters, and `LinearLayout.LayoutParams implements additional parameters specific
to LinearLayout, as for example the layout_weight parameter.

Animation API’s
These classes provide functionality for the property animation system, which allows you to
animate object properties of any type. int, float, and hexadecimal color values are supported
by default. You can animate any other type by telling the system how to calculate the values for
that given type with a custom TypeEvaluator.
You can set many different types of interpolators (contained in android.view.animation),
specify keyframes, or group animations to play sequentially or simultaneously (with
AnimatorSet) to further control your animation behaviors.

Interfaces

Animator.AnimatorListener
An animation listener receives notifications from an
animation.
A pause listener receives notifications from an animation
Animator.AnimatorPauseListener
when the animation is paused or resumed.
This interface is used for listening to starting and ending
LayoutTransition.TransitionListener
events for transitions.
Implementors of this interface can set themselves as update
listeners to a TimeAnimator instance to receive callbacks on
TimeAnimator.TimeListener
every animation frame to receive the total time since the
animator started and the delta time since the last frame.
A time interpolator defines the rate of change of an
TimeInterpolator
animation.
Interface for use with the setEvaluator(TypeEvaluator)
TypeEvaluator<T>
function.
Implementors of this interface can add themselves as update
listeners to an ValueAnimator instance to receive callbacks
ValueAnimator.AnimatorUpdateListener
on every animation frame, after the current frame's values
have been calculated for that ValueAnimator.

Classes
This is the superclass for classes which provide basic support for
Animator animations which can be started, ended, and have
AnimatorListeners added to them.
This class is used to instantiate animator XML files into Animator
AnimatorInflater
objects.
This adapter class provides empty implementations of the methods
AnimatorListenerAdapter
from Animator.AnimatorListener.
AnimatorSet This class plays a set of Animator objects in the specified order.
The Builder object is a utility class to facilitate adding animations to
AnimatorSet.Builder a AnimatorSet along with the relationships between the various
animations.
This evaluator can be used to perform type interpolation between
ArgbEvaluator
integer values that represent ARGB colors.
Abstract base class used convert type T to another type V and back
BidirectionalTypeConverter<T, V>
again.
This evaluator can be used to perform type interpolation between
FloatArrayEvaluator
float[] values.
This evaluator can be used to perform type interpolation between
FloatEvaluator
float values.
This evaluator can be used to perform type interpolation between
IntArrayEvaluator
int[] values.
This evaluator can be used to perform type interpolation between int
IntEvaluator
values.
Keyframe This class holds a time/value pair for an animation.
This class enables automatic animations on layout changes in
LayoutTransition
ViewGroup objects.
This subclass of ValueAnimator provides support for animating
ObjectAnimator
properties on target objects.
This evaluator can be used to perform type interpolation between
PointFEvaluator
PointF values.
This class holds information about a property and the values that that
PropertyValuesHolder
property should take on during an animation.
This evaluator can be used to perform type interpolation between
RectEvaluator
Rect values.
Lets you define a number of Animators that will run on the attached
StateListAnimator
View depending on the View's drawable state.
This class provides a simple callback mechanism to listeners that is
TimeAnimator
synchronized with all other animators in the system.
TypeConverter<T, V> Abstract base class used convert type T to another type V.
This class provides a simple timing engine for running animations
ValueAnimator
which calculate animated values and set them on target objects.

You might also like