Viewをグルグル回転させて初期角度で止めるには (How to reset angle rotation to default?)

Viewを回転させるにはいくつか方法がありますが、今回はリピート設定のないViewPropertyAnimatorを使います。回転を始めて、ストップした時にすぐに止めないで、終了リスナーを上書きしてリピートされないようにしておきます。

通信中にNow Loadingで回転させるボタンを作ってみました。

class ProgressButton @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyle: Int = 0
) : MaterialButton(context, attrs, defStyle) {

    fun startRotating(duration: Long = 1200, angle: Float = 360f) {
        var runnable: Runnable? = null
        runnable = Runnable {
            animate()
                .rotationBy(angle)
                .setDuration(duration)
                .setInterpolator(LinearInterpolator())
                // .withEndAction can not be changed during the animation
                .setListener(object: Animator.AnimatorListener {
                    override fun onAnimationStart(animation: Animator?) {}
                    override fun onAnimationCancel(animation: Animator?) {}
                    override fun onAnimationRepeat(animation: Animator?) {}
                    override fun onAnimationEnd(animation: Animator?) { runnable?.run() }
                })
                .start()
        }

        isClickable = false
        runnable.run()
    }

    fun stopRotating() {
        animate()
            .setListener(object: Animator.AnimatorListener {
                override fun onAnimationStart(animation: Animator?) {}
                override fun onAnimationCancel(animation: Animator?) {}
                override fun onAnimationRepeat(animation: Animator?) {}
                override fun onAnimationEnd(animation: Animator?) { isClickable = true }
            })
    }
}

ちなみに、リスナーのonAnimationCancelは不測の事態でキャンセルされた時に呼ばれるものではなく、animate().cancel()で停止させた時に呼ばれるもので、onAnimationCancel -> onAnimationEnd、の順で呼ばれます。