Introduction
Android includes many options to add animations to your app. In this tutorial, we will learn how to add a type of animation called frame-by-frame animation into our Android app.
Goals
At the end of the tutorial, you would have learned:
- How to add frame-by-frame animation to an Android app.
Tools Required
- Android Studio. The version used in this tutorial is Android Studio Dolphin | 2021.3.1.
Prerequisite Knowledge
- Intermediate Android.
Project Setup
To follow along with the tutorial, perform the steps below:
-
Create a new Android project with the default Empty Activity.
-
Download the free Santa sprites from Game Art 2d.
-
Unpack the archive and import all files in the png directory to the project (keep the same names that Android Studio generated for you).
-
Copy and paste all the
<string>
resources below into your project’s strings.xml file.<string name="idle">Idle</string> <string name="walk">Walk</string> <string name="run">Run</string> <string name="jump">Jump</string> <string name="die">Die</string> <string name="slide">Slide</string>
-
Replace all the content of activity_main.xml with the code below.
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <ImageView android:id="@+id/image_santa" android:layout_width="0dp" android:layout_height="300dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" android:src="@drawable/idle__1_" /> <Button android:id="@+id/idle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="@string/idle" app:layout_constraintBottom_toTopOf="@id/walk" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/image_santa" /> <Button android:id="@+id/walk" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/walk" app:layout_constraintBottom_toTopOf="@id/run" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/idle" /> <Button android:id="@+id/run" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/run" app:layout_constraintBottom_toTopOf="@id/jump" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/walk" /> <Button android:id="@+id/jump" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/jump" app:layout_constraintBottom_toTopOf="@id/slide" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/run" /> <Button android:id="@+id/slide" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/slide" app:layout_constraintBottom_toTopOf="@id/die" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/jump" /> <Button android:id="@+id/die" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/die" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/slide" /> </androidx.constraintlayout.widget.ConstraintLayout>
Project Overview
Our starter application contains a single screen with 6 buttons, allowing the user to start animations of the following actions
- Idle.
- Walk.
- Run.
- Jump.
- Slide.
- Die.
There are two steps that we need to do next to allow the animation to work:
- Set up the animation resources that describe the frame-by-frame image.
- Set up button listeners to activate the animation.
Set Up Animation Resource XML
The type of animation that we are aiming for can be achieved by loading images in a sequence. We can either define this animation in code using the AnimationDrawable class or using a pre-defined Frame Animation Resource. The frame animation resource starts with an <animation-list>
tag, and then each image is defined in an <item>
tag.
To create the frame animation resource for the idle action, create a new file called idle.xml and copy and paste the code below into it.
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/idle__1_" android:duration="75" />
<item android:drawable="@drawable/idle__2_" android:duration="75" />
<item android:drawable="@drawable/idle__3_" android:duration="75" />
<item android:drawable="@drawable/idle__4_" android:duration="75" />
<item android:drawable="@drawable/idle__5_" android:duration="75" />
<item android:drawable="@drawable/idle__6_" android:duration="75" />
<item android:drawable="@drawable/idle__7_" android:duration="75" />
<item android:drawable="@drawable/idle__8_" android:duration="75" />
<item android:drawable="@drawable/idle__9_" android:duration="75" />
<item android:drawable="@drawable/idle__10_" android:duration="75" />
<item android:drawable="@drawable/idle__11_" android:duration="75" />
<item android:drawable="@drawable/idle__12_" android:duration="75" />
<item android:drawable="@drawable/idle__13_" android:duration="75" />
<item android:drawable="@drawable/idle__14_" android:duration="75" />
<item android:drawable="@drawable/idle__15_" android:duration="75" />
<item android:drawable="@drawable/idle__16_" android:duration="75" />
</animation-list>
Each <item>
must contain the source to the actual drawable resource (the images that we imported earlier). The duration
attribute dictates how long to show a frame, in milliseconds.
If you switch to Split/Design view, you can also preview the animation.
Now that we already know how to define the idle animation resource, create the rest of the animations with the names below.
-
walk.xml.
<?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/walk__1_" android:duration="75" /> <item android:drawable="@drawable/walk__2_" android:duration="75" /> <item android:drawable="@drawable/walk__3_" android:duration="75" /> <item android:drawable="@drawable/walk__4_" android:duration="75" /> <item android:drawable="@drawable/walk__5_" android:duration="75" /> <item android:drawable="@drawable/walk__6_" android:duration="75" /> <item android:drawable="@drawable/walk__7_" android:duration="75" /> <item android:drawable="@drawable/walk__8_" android:duration="75" /> <item android:drawable="@drawable/walk__9_" android:duration="75" /> <item android:drawable="@drawable/walk__10_" android:duration="75" /> <item android:drawable="@drawable/walk__11_" android:duration="75" /> <item android:drawable="@drawable/walk__12_" android:duration="75" /> <item android:drawable="@drawable/walk__13_" android:duration="75" /> </animation-list>
-
run.xml.
<?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/run__1_" android:duration="75" /> <item android:drawable="@drawable/run__2_" android:duration="75" /> <item android:drawable="@drawable/run__3_" android:duration="75" /> <item android:drawable="@drawable/run__4_" android:duration="75" /> <item android:drawable="@drawable/run__5_" android:duration="75" /> <item android:drawable="@drawable/run__6_" android:duration="75" /> <item android:drawable="@drawable/run__7_" android:duration="75" /> <item android:drawable="@drawable/run__8_" android:duration="75" /> <item android:drawable="@drawable/run__9_" android:duration="75" /> <item android:drawable="@drawable/run__10_" android:duration="75" /> <item android:drawable="@drawable/run__11_" android:duration="75" /> </animation-list>
-
jump.xml.
<?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/jump__1_" android:duration="75" /> <item android:drawable="@drawable/jump__2_" android:duration="75" /> <item android:drawable="@drawable/jump__3_" android:duration="75" /> <item android:drawable="@drawable/jump__4_" android:duration="75" /> <item android:drawable="@drawable/jump__5_" android:duration="75" /> <item android:drawable="@drawable/jump__6_" android:duration="75" /> <item android:drawable="@drawable/jump__7_" android:duration="75" /> <item android:drawable="@drawable/jump__8_" android:duration="75" /> <item android:drawable="@drawable/jump__9_" android:duration="75" /> <item android:drawable="@drawable/jump__10_" android:duration="75" /> <item android:drawable="@drawable/jump__11_" android:duration="75" /> <item android:drawable="@drawable/jump__12_" android:duration="75" /> <item android:drawable="@drawable/jump__13_" android:duration="75" /> <item android:drawable="@drawable/jump__14_" android:duration="75" /> <item android:drawable="@drawable/jump__15_" android:duration="75" /> <item android:drawable="@drawable/jump__16_" android:duration="75" /> </animation-list>
-
slide.xml
<?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/slide__1_" android:duration="75" /> <item android:drawable="@drawable/slide__2_" android:duration="75" /> <item android:drawable="@drawable/slide__3_" android:duration="75" /> <item android:drawable="@drawable/slide__4_" android:duration="75" /> <item android:drawable="@drawable/slide__5_" android:duration="75" /> <item android:drawable="@drawable/slide__6_" android:duration="75" /> <item android:drawable="@drawable/slide__7_" android:duration="75" /> <item android:drawable="@drawable/slide__8_" android:duration="75" /> <item android:drawable="@drawable/slide__9_" android:duration="75" /> <item android:drawable="@drawable/slide__10_" android:duration="75" /> <item android:drawable="@drawable/slide__11_" android:duration="75" /> </animation-list>
-
die.xml.
<?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/dead__1_" android:duration="75" /> <item android:drawable="@drawable/dead__2_" android:duration="75" /> <item android:drawable="@drawable/dead__3_" android:duration="75" /> <item android:drawable="@drawable/dead__4_" android:duration="75" /> <item android:drawable="@drawable/dead__5_" android:duration="75" /> <item android:drawable="@drawable/dead__6_" android:duration="75" /> <item android:drawable="@drawable/dead__7_" android:duration="75" /> <item android:drawable="@drawable/dead__8_" android:duration="75" /> <item android:drawable="@drawable/dead__9_" android:duration="75" /> <item android:drawable="@drawable/dead__10_" android:duration="75" /> <item android:drawable="@drawable/dead__11_" android:duration="75" /> <item android:drawable="@drawable/dead__12_" android:duration="75" /> <item android:drawable="@drawable/dead__12_" android:duration="75" /> <item android:drawable="@drawable/dead__13_" android:duration="75" /> <item android:drawable="@drawable/dead__14_" android:duration="75" /> <item android:drawable="@drawable/dead__15_" android:duration="75" /> <item android:drawable="@drawable/dead__16_" android:duration="75" /> <item android:drawable="@drawable/dead__17_" android:duration="75" /> </animation-list>
Start The Animation In Code
To use the animations that we have defined, first, in onCreate()
, get the reference to the ImageView that displays Santa.
val santaImage = findViewById<ImageView>(R.id.image_santa)
We have six buttons in total, calling findViewById()
6 different times would be a chore, so let us use a Map to reduce repetitive code and improve readability.
// Add Button IDs and Animation IDs into map for easy looping.
val animationMap = mapOf(
Pair(R.id.idle, R.drawable.idle),
Pair(R.id.walk, R.drawable.walk),
Pair(R.id.run, R.drawable.run),
Pair(R.id.jump, R.drawable.jump),
Pair(R.id.slide, R.drawable.slide),
Pair(R.id.die, R.drawable.die)
)
for ((buttonId, animationId) in animationMap){
findViewById<Button>(buttonId).setOnClickListener {
santaImage.setImageResource(animationId)
(santaImage.drawable as AnimationDrawable).start()
}
}
The two most important lines of code above are
santaImage.setImageResource(animationId)
(santaImage.drawable as AnimationDrawable).start()
- Every time we need to play a different animation, we need to replace the image resource with the corresponding animation resource.
- We then cast the
drawable
to AnimationDrawable and then callstart()
to start the animation.
Run The App
We are now ready to run the app, it should behave similarly to the animation below.
Summary
We have learned how to animate sprites in this tutorial. The full project code can be found at https://github.com/dmitrilc/DaniwebAnimateSpritesAnimationDrawable.