Android Programming In Kotlin: Animation
Written by Mike James   
Monday, 12 March 2018
Article Index
Android Programming In Kotlin: Animation
runOnUiThread(Runnable)

You can animate Android graphics using nothing but a timer. It's not the best way to do the job for anything complex , but it's good to know it can be done. This extract is taken from Beginning Bitmap Graphics in a new book aimed at Android programmers wanting to use Kotlin.

 

Android Programming In Kotlin
Starting with an App

Covers Android Studio 3 and Constraint Layout.

Is now available as a print book:

coverKotlinsmall

Buy from: Amazon

Contents

  1. Getting Started With Android Studio 3
  2. The Activity And The UI
        Extract: Activity & UI  
  3. Building The UI and a Calculator App
        Extract: A First App
  4. Android Events
  5. Basic Controls
        Extract Basic Controls
        Extract More Controls ***NEW!
  6. Layout Containers
        Extract Layouts - LinearLayout
  7. The ConstraintLayout 
        Extract Bias & Chains
  8. Programming The UI
        Extract Programming the UI
        Extract Layouts and Autonaming Components
  9. Menus & The Action Bar
  10. Menus, Context & Popup
  11. Resources
        Extract Conditional Resources
  12. Beginning Bitmap Graphics
        Extract Animation
  13. Staying Alive! Lifecycle & State
        Extract  State Managment
  14. Spinners
  15. Pickers
  16. ListView And Adapters
  17. Android The Kotlin Way

If you are interested in creating custom template also see:

Custom Projects In Android Studio

Androidgears

 

Simple Animation

To bring this chapter to a close we will animate a ball bouncing around a Canvas, or a Bitmap depending how you look at it.

This might seem like a strange topic to end on, especially since we are not going to do the job in the way that most Android programmers would go about it. Indeed Android has a range of different animation facilities – View animation, Value animation and so on.

However, none of them demonstrates the fundamental way that dynamic graphics work and before you move on to learn more sophisticated ways of creating animation it is a good idea to find out how things work at the lowest level.

This example not only teaches you something about animation, but also about the problems of creating dynamic graphics of any kind in the Android UI.

One warning – do not assume this is all there is to know about Android animation or that this is the best way to do things.

To animate something in the simplest and most direct way all you have to do is draw the shape, change the shape, erase the old graphic, and draw it again. 

In most systems this is usually achieved at the lowest possible level by using a timer to call an update function which erases the shape, does the update to the shape and then draws it at its new location. You can take this approach in Android, but for various reasons it isn't the way things are usually done. It has to be admitted that there are some slight difficulties, but overcoming them isn't hard and is very instructive.

To see how it all works let's just bounce a "ball" around the screen. This is more or less the "hello world" of simple 2D sprite-based graphics.

So start a new Android Studio project and place an ImageView on the design surface. This is the only UI element we need. 

We need a set of objects and values that are accessible from a number of methods and that have a lifetime the same as the app. The simplest way of achieving this is to set up private properties:

private val b = Bitmap.createBitmap(width, height,
                           Bitmap.Config.ARGB_8888)
private val c: Canvas=Canvas(b) private val paint: Paint = Paint()

First we create a bitmap and associate it with a Canvas. The Paint object is created to avoid having to create an instance every time we update the graphics.

Notice that other parts of the program are going to need to access width, height of the play area:

private val width = 800
private val height = 800

We are also going to need properties to record the ball's position, its radius and velocity. For simplicity we might as well just use the default pixel coordinates of the Bitmap:

private var x = 463f
private var y = 743f
private var vx = 1f
private var vy = 1f
private var r = 30f

Now we have all of these variables defined we can move on with the OnCreate function and set up the color of the play area and the Paint object used to draw the ball:

c.drawColor(Color.WHITE)
paint.setAntiAlias(false)
paint.style = Paint.Style.FILL

You might be wondering why AntiAlias is set to false, i.e. turned off. The reason is that its dithering algorithm makes it hard to remove a graphic by redrawing it in the background color. Try changing false to true in the final program to see what the problem is. 

We also need to set the bitmap we are drawing on to the display: 

imageView.setImageBitmap(b)

Now we are all ready to start drawing the animation.

Timer and Threads

Now we come to the inner workings of the animation.

We need a Timer object that runs a function every so many milliseconds:

val timer = Timer()

The timer object has a range of schedule functions which run a function, actually a method in a TimerTask object, at different times. The one we need is:

timer.schedule(TimerTask,delay,repeat)

which runs the TimerTask after delay milliseconds and every repeat milliseconds after that. The timings aren't accurate and it could take longer than specified for the TimerTask to be run.

The simplest way to create the TimerTask is to use an object expression. You can’t use a lambda because the TimerTask is an object with a constructor and some additional methods i.e. it isn’t a SAM:

timer.schedule(object : TimerTask() {
                  override fun run() {
                             update()
                           }
               } , 0, 10)

This creates a new TimerTask and overrides its run method. The run method is called when the Timer is triggered. All it does is to call the new function update, which we have yet to write, that does the update to the ball's position etc.

The final two parameters specify a 0 millisecond delay in triggering the first call and then 10 milliseconds as the repeat period. That is, update will be called every 10 milliseconds or so. If the processor is busy doing something else it could be more than 10 milliseconds between repeats. 

The update function is fairly easy:

fun update() {
   paint.color=Color.WHITE
   c.drawCircle(x, y, r, paint)
   x = x + vx
   y = y + vy
   if (x + r >= width) vx = -vx
   if (x - r <= 0) vx = -vx
   if (y + r >= height) vy = -vy
   if (y - r <= 0) vy = -vy
   paint.color=Color.RED
   c.drawCircle(x, y, r, paint)
   imageView.invalidate()
}

First it sets the color to white and draws the ball, a circle. This erases the ball at its old position, remember the background is white. Next it updates the position by adding the velocities in each direction. To make sure that the ball bounces we test to see if it has reached a boundary and if it has its velocity is reversed. Finally, the color is set to red and the ball is drawn at the new position. 

If the function was to stop at this point then everything compiles and runs, but you won't see the ball move. The reason is simply that the UI is drawn once at when the program is initially run and then only when it is necessary because the user has interacted with it or the orientation has changed, etc. As a result the bitmap displayed by the ImageView object would be changed every 10 milliseconds, but it would not be redisplayed. 

To make the UI update we need to call the ImageView's invalidate method which basically tells the UI to redraw it. However, if you put this in at the end of the update function you get an error message something like:

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

The reason for this is that the Timer object uses a new thread to run the TimerTask. This is often what you want to happen, but in this case it is a problem. It is a problem that often occurs in creating a sophisticated Android app and something you are going to have to learn to cope with.

If you are new to the idea of threading, this explanation might help.

When you run an Activity it is assigned a single thread or execution. A thread is a unit of execution and it is what obeys your instructions. In a complete system there are many threads of execution – some running and some suspended. The operating system picks which threads get to run in a way that attempts to make them all seem to be making progress.

The single thread that the Activity gets is generally called the UI thread because its job is just to take care of the UI. It responds to events from the Activity like OnCreate and from the user like a Button click. When the UI thread responds to an event it obeys the event handler and then goes back to waiting for the next event. This is the sense in which every Android app is simply a collection of event handlers that the UI thread executes when the corresponding event occurs.

The big problem is that the UI event is really only happy when it has nothing to do. Then it just waits for an event and processes it at once. This makes the user think your app is very responsive because clicks and other input are acted on at once. If you give the UI thread a long task to do, for example you write a lot of processing into an event handler, then it isn't just waiting for the user to do something, and the user starts to think that your app is slow and sluggish. At the extreme the UI thread can be kept 100% busy doing something and then the entire UI seems to freeze up.

In short the UI thread should not be used for intensive computation or anything that takes more than a few milliseconds. The way to achieve this is to use other threads. This is a main topic of Android Programming: Structuring Complex Apps.



Last Updated ( Monday, 14 May 2018 )