Home » Android » android – Kotlin – Multiple animations at the same time and/or repeating after each other-Exceptionshub

android – Kotlin – Multiple animations at the same time and/or repeating after each other-Exceptionshub

Posted by: admin February 26, 2020 Leave a comment

Questions:

I am trying to create a game app in Android Studio. The concept is: there are circles falling down and you need to click/press them to make them disappear before touching a line at the bottom of the screen. To do this I create an ImageView with a circle as an image. I have it appear at the top and it moves down at a slow speed. When I press it, it disappears. The problem is, the second circle appears after the first which is good, but the downwards animation is not starting and the OnClickListener is not working either.

Here is the code for the creation of a circle:

/*Creation of ball via imageView.
ImageView is created on the current Activity Layout with the circle as image.
ImageView is 200x200 and appears in the middle of the screen (horizontal) on top.
*/
private fun drawBall(){
    val imageView = ImageView(this)
    imageView.setImageDrawable(getDrawable(R.drawable.ball_1))
    imageView.layoutParams = LinearLayout.LayoutParams(200, 200)
    imageView.x = (width/2.5).toFloat()
    layout.addView(imageView)
    moveBall(imageView)
}

Here is the code for the animation to start and the OnClickListener for the circle to disappear:

//Animation of ball falling down.
private fun moveBall(imageView: ImageView){
    val valueAnimator = ValueAnimator.ofFloat(0f, height*0.68.toFloat() )
    valueAnimator.addUpdateListener {
        val value = it.animatedValue as Float
        imageView.translationY = value
    }

    valueAnimator.interpolator = LinearInterpolator()
    valueAnimator.duration = 2500
    valueAnimator.start()

    //Shooting the ball and incrementing score
    imageView.setOnClickListener(){
        imageView.visibility = View.INVISIBLE
        var currentScore = textView_score.text.toString().toInt()
        currentScore++
        textView_score.text = currentScore.toString()
    }
}

Here you find the code where I’m trying to create multiple instances of a circle to fall down:

//Multiple instances of the falling ball animation.
private fun startBalls(){
    val runnable = Runnable { drawBall() }
    runnable.run(){
        drawBall()
    }

    val exec = ScheduledThreadPoolExecutor(1)
    val period : Long = 1000
    exec.scheduleAtFixedRate(runnable,0, period, TimeUnit.MILLISECONDS)
    val delay : Long = 1000
    exec.scheduleWithFixedDelay(runnable, 0, delay, TimeUnit.MILLISECONDS)
}

I think my main problem is where I’m trying to create the multiple instances of the circles.
Thank you in advance.

How to&Answers:

Logging from moveBall() shows that the code is not executed on the UI thread when the Runnable is started by the ScheduledThreadPoolExecutor. More specifically, the next line after valueAnimator.start() is not executed at all (the first time you “touch” a UI element causes an Exception in the non-UI thread”), so the OnClickListener is not set for all but the first ImageView.

Since it also seems to be a problem when the same instance of a Runnable is reused with ScheduledThreadPoolExecutor, you can use a custom Runnable class and have it invoke drawBall() on the UI thread:

class MyRunnable(val functionToInvoke: () -> Unit): Runnable {
    override fun run() {
        Handler(Looper.getMainLooper()).post(Runnable {
            //this will run on the UI thread
            functionToInvoke.invoke()
      })
    }
}

Usage in startBalls():

val exec = ScheduledThreadPoolExecutor(1)
val period : Long = 1000
exec.scheduleAtFixedRate(MyRunnable({drawBall()}),0, period, TimeUnit.MILLISECONDS)
val delay : Long = 1000
exec.scheduleWithFixedDelay(MyRunnable({drawBall()}), 0, delay, TimeUnit.MILLISECONDS)