I can't figure out what is wrong in here, looks like Timer is causing this issue but even if I remove Timer line completely it still crashing with error like thi "Only the original thread that created a view hierarchy can touch its views."
Maybe you guys can suggest a different way to run animations inside of an activity to avoid this.
Another interesting thing is that it runs perfectly fine on Android 12, but crashing on Android 7.
Here is the code that sitting inside of Activity:
private fun setAnimations() {
fadeInErrorMessage.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationStart(animation: Animation) {
binding.mainErrorMessageContainer.visibility = View.VISIBLE
}
override fun onAnimationEnd(animation: Animation) {
Timer("ErrorMessage", false).schedule(1500L) {
binding.mainErrorMessageContainer.startAnimation(fadeOutErrorMessage)
}
}
override fun onAnimationRepeat(animation: Animation) {}
})
fadeOutErrorMessage.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationStart(animation: Animation) {}
override fun onAnimationEnd(animation: Animation) {
binding.mainErrorMessageContainer.visibility = View.INVISIBLE
errorMessageAnimationIsRunning = false
}
override fun onAnimationRepeat(animation: Animation) {}
})
}
Here is logs:
2022-09-14 21:24:22.981 17059-17371/com.company.app E/AndroidRuntime: FATAL EXCEPTION: ErrorMessage
Process: com.company.app, PID: 17059
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6892)
at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:1083)
at android.view.ViewGroup.invalidateChild(ViewGroup.java:5205)
at android.view.View.invalidateInternal(View.java:13657)
at android.view.View.invalidate(View.java:13621)
at android.view.View.startAnimation(View.java:20175)
at com.company.app.ui.main.MainActivity$setAnimations$1$onAnimationEnd$$inlined$schedule$1.run(Timer.kt:149)
at java.util.TimerThread.mainLoop(Timer.java:555)
at java.util.TimerThread.run(Timer.java:505)
CodePudding user response:
Just run your animations inside runOnUiThread()
:
runOnUiThread(new Runnable() {
void run() {
binding.mainErrorMessageContainer.visibility = View.VISIBLE
}
});
runOnUiThread(new Runnable() {
void run() {
binding.mainErrorMessageContainer.startAnimation(fadeOutErrorMessage)
}
});
etc.
CodePudding user response:
override fun onAnimationEnd(animation: Animation) {
Timer("ErrorMessage", false).schedule(1500L) {
binding.mainErrorMessageContainer.startAnimation(fadeOutErrorMessage)
}
}
You can't do it on a timer like that. That causes it to happen on a different thread. Assuming you created the UI on the main thread, put the body of the timer (the binding.mainError part) inside a runOnUiThread block. If you created the UI on a different thread, you should probably fix that first.
You can tell that its the endAnimation call that's causing the problem because it's the one calling startAnimation, and its the one that's in the stack trace.