📜 ⬆️ ⬇️

Animations in Android on the shelves (Part 3. "Low-level" animation)

Part 1. Basic animations
Part 2. Complex animations
Part 3. "Low-level" animation
Part 4. Transition Animations
Part 5. Libraries for working with animation

All the methods discussed in the previous sections are good and convenient, but if we need to animate a large number of objects, they may not be suitable. In this part, we will look at ways that allow us to work with a really large number of objects and create programmatically complex animations.

Part 3. "Low-level" animation


1. Drawing on canvas View


image

The first way we’ll look at is drawing in the onDraw method of our View object. This method is implemented simply by simply overriding onDraw and finally calling postInvalidateOnAnimation() .
')
In this example, our drawable will move along the x axis.

 override fun onDraw(canvas: Canvas) { super.onDraw(canvas) x += resources.getDimension(R.dimen.speed) drawable.setBounds(x, y, x + size, y + size) drawable.draw(canvas) postInvalidateOnAnimation() } 

The example with snowflakes above will take a bit more code, since we need to keep the state of each individual snowflake separately.

Show me the code!
 class SnowAnimation : View { ... private lateinit var snowflakes: Array<Snowflake> override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { snowflakes = Array(10, { Snowflake(right - left, bottom - top, context.getDrawable(R.drawable.snowflake), resources.getDimension(R.dimen.max_snowflake_size), resources.getDimension(R.dimen.max_snowflake_speed)) }) } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) snowflakes.forEach { it.update() it.draw(canvas) } postInvalidateOnAnimation() } } 

 internal class Snowflake(private val containerWidth: Int, private val containerHeight: Int, private val drawable: Drawable, private val maxSize: Float, private val maxSpeed: Float) { private var size: Double = 0.0 private var speed: Double = 0.0 private var x: Double = 0.0 private var y: Double = 0.0 init { reset() } private fun reset() { size = Math.random() * maxSize / 2 + maxSize / 2 speed = Math.random() * maxSpeed / 2 + maxSpeed / 2 y = -size; x = Math.random() * containerWidth } fun update() { y += speed if (y > containerHeight) { reset() } } fun draw(canvas: Canvas?) { if (canvas == null) { return } drawable.setBounds(x.toInt(), y.toInt(), (x + size).toInt(), (y + size).toInt()) drawable.draw(canvas) } } 


Application:


Advantages:


Disadvantages:


2. Drawing on the SurfaceView canvas


What if the calculation of the next step of the animation will take considerable time? We can still use the first method and make calculations in a separate stream. But it still does not lead to 100% smoothness in the animation. UI thread can be loaded more than any other than our animation.

Android allows you to get rid of the main loop (main loop) of drawing using the component SurfaceView . And since we are no longer tied to the main loop, we will have to keep our flow for calculations and rendering. SurfaceView provides callbacks in which we can start and stop our stream. In the stream at the end of the calculations, we will draw our animation.

The implementation of the same animation of snowflakes will look like this:

Show me the code!
 class MySurfaceView : SurfaceView, SurfaceHolder.Callback { ... private lateinit var drawThread: DrawThread; init { holder.addCallback(this) } override fun surfaceCreated(holder: SurfaceHolder) { //    surface drawThread = DrawThread(getHolder(), context, measuredWidth, measuredHeight) drawThread.start() } override fun surfaceDestroyed(holder: SurfaceHolder) { var retry = true //    surface drawThread.cancel(); //          , ,   .            . while (retry) { try { drawThread.join() retry = false } catch (e: InterruptedException) { } } } } internal class DrawThread(private val surfaceHolder: SurfaceHolder, context: Context, width: Int, height: Int) : Thread() { private var snowflakes: Array<Snowflake> private var cancelled: Boolean = false init { snowflakes = Array(10, { Snowflake(width, height, context.getDrawable(R.drawable.snowflake), context.resources.getDimension(R.dimen.max_snowflake_size), context.resources.getDimension(R.dimen.max_snowflake_speed)) }) } override fun run() { while (!cancelled) { // canvas    var canvas: Canvas? = surfaceHolder.lockCanvas() try { //   onDraw  View      ,         ,    . canvas?.drawColor(Color.WHITE) snowflakes.forEach { it.update() it.draw(canvas) } } finally { if (canvas != null) { // canvas   surfaceHolder.unlockCanvasAndPost(canvas) } } } } fun cancel() { cancelled = true } } 


Application:


Advantages:


Disadvantages:


3. OpenGL


image
Just like on canvas, we can draw using the OpenGL API. If you have conceived something more complicated than a cube in the picture, then you should look in the direction of some engine, for example libgdx . Unfortunately, even the basic example will take quite a lot of space here, so we confine ourselves only to this brief preview.

Application:


Advantages:


Disadvantages:


All examples can be viewed and studied here .

Source: https://habr.com/ru/post/348664/


All Articles