Professional Documents
Culture Documents
Graphics and Animation
Graphics and Animation
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:
In addition to these 2D graphics techniques, Android also provides several different ways to
create animations:
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 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:
<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);
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:
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 .
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 :
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):
The following XML shows an example of an animation file that combines some of these
elements:
<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:
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.)
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:
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:
Now that we have an understanding of ValueAnimator, lets learn more about 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:
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;
_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);
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.
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.
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.