Android Native - Animate Sprites Using AnimationDrawable

dimitrilc 1 Tallied Votes 325 Views Share

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:

  1. How to add frame-by-frame animation to an Android app.

Tools Required

  1. Android Studio. The version used in this tutorial is Android Studio Dolphin | 2021.3.1.

Prerequisite Knowledge

  1. Intermediate Android.

Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Download the free Santa sprites from Game Art 2d.

  3. Unpack the archive and import all files in the png directory to the project (keep the same names that Android Studio generated for you).

  4. 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>
  5. 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

  1. Idle.
  2. Walk.
  3. Run.
  4. Jump.
  5. Slide.
  6. Die.

Screenshot_1665435838.png

There are two steps that we need to do next to allow the animation to work:

  1. Set up the animation resources that describe the frame-by-frame image.
  2. 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.

Screen_Shot_2022-10-10_at_5.50.14_PM.png

Now that we already know how to define the idle animation resource, create the rest of the animations with the names below.

  1. 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>
  2. 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>
  3. 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>
  4. 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>
  5. 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()
  1. Every time we need to play a different animation, we need to replace the image resource with the corresponding animation resource.
  2. We then cast the drawable to AnimationDrawable and then call start() to start the animation.

Run The App

We are now ready to run the app, it should behave similarly to the animation below.

Daniweb_animated_Sprites.gif

Summary

We have learned how to animate sprites in this tutorial. The full project code can be found at https://github.com/dmitrilc/DaniwebAnimateSpritesAnimationDrawable.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.