ObjectAnimator

https://developer.android.com/guide/topics/graphics/prop-animation.html#views

 val animX = ObjectAnimator.ofFloat(myView, "x", 50f)
    val animY = ObjectAnimator.ofFloat(myView, "y", 100f)
    AnimatorSet().apply {
        playTogether(animX, animY)
        start()
    }

https://developer.android.com/reference/androidx/core/animation/ObjectAnimator

https://wiki.jikexueyuan.com/project/android-animation/7.html

ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"alpha",1,0,1);  
animator.setDuration(2000);  
animator.start(); 


ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"rotation",0,180,0);  
animator.setDuration(2000);  
animator.start();

ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"rotationX",0,270,0);  
animator.setDuration(2000);  
animator.start();

ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"rotationY",0,180,0);  
animator.setDuration(2000);  
animator.start();


ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"rotation",0,270,0);  
animator.setDuration(2000);  
animator.start(); 

ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "translationX", 0, 200, -200,0);  
animator.setDuration(2000);  
animator.start();  

ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "scaleX", 0, 3, 1);  
animator.setDuration(2000);  
animator.start();

ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "scaleY", 0, 3, 1);  
animator.setDuration(2000);  
animator.start(); 

CoroutineScope & MainScope

https://stackoverflow.com/questions/70208380/mainscope-vs-globalscope

MainScope is a CoroutineScope that uses the Dispatchers.Main dispatcher by default, which is bound to the main UI thread.

The GlobalScope is a CoroutineScope that has no dispatcher in its coroutine context. This means that the coroutines launched in this scope will use the Dispatchers.Default dispatcher, which is backed by a thread pool (sized based on the number of CPU cores you have).

The GlobalScope also has no Job in its context, which means structured concurrency doesn’t apply. Coroutines launched in it are never cancelled automatically, so they need to be manually controlled. This is why it is usually discouraged to use it unless you have very specific needs.

Only the original thread that created a view hierarchy can touch its views.

This error occurs when you’re trying to modify views from outside the main thread, which is what happens if you do this from coroutines launched in the GlobalScope (because it’s backed by a separate thread pool).

In your second snippet, you are using withContext(Dispatchers.Default), which only makes this part of the code run on that thread pool, but the rest runs on UI thread. This is why the update of the UI is ok there.

Note that Room already uses a dispatcher with a background thread pool for its queries, so you don’t need to switch context manually like this, you can just call it from the UI thread.

Side note: using MainScope().launch { .. } like this is a bad idea, because it suffers from the same cancellation issue as GlobalScope. To use it properly, you would need to extract this scope into a variable/property so you can cancel it when appropriate. That said, it’s easier to use an existing scope. Android already provides a ready-to-use coroutine scope in components such as Activities which have a lifecycle (see lifecycle-runtime-ktx library). It’s called lifecycleScope. You should launch your coroutines in this scope so they automatically get cancelled when the activity is destroyed.