Spring Animations

ANIMATION

Spring Animation with kotlin in Android

A complete guide for physics based animations.

Spring animation Gif

Prerequisite :

android {
...

dataBinding {
enabled = true
}

}
apply plugin: 'kotlin-kapt'
dependencies {
...

implementation 'com.google.android.material:material:1.1.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
implementation 'androidx.dynamicanimation:dynamicanimation:1.0.0'

}
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity">

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true">

<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/floatingBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@drawable/ic_add_black_24dp"/>

</RelativeLayout>

</layout>

Our steps will be :

private fun createSpringAnimation(view: View,
property: DynamicAnimation.ViewProperty,
finalPosition: Float): SpringAnimation {
val animation = SpringAnimation(view, property)
val spring = SpringForce(finalPosition)
spring.stiffness = SpringForce.STIFFNESS_MEDIUM
spring.dampingRatio = SpringForce.DAMPING_RATIO_HIGH_BOUNCY
animation.spring = spring
return animation
}
fun setUpAnimation(view: View, callback: () -> Unit) {

lateinit var xAnimation: SpringAnimation
lateinit var yAnimation: SpringAnimation

view.viewTreeObserver.addOnGlobalLayoutListener(object: ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
xAnimation = createSpringAnimation(view, SpringAnimation.X, view.x)
yAnimation = createSpringAnimation(view, SpringAnimation.Y, view.y)
view.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
})

var dX = 0f
var dY = 0f
val gestureDetector = GestureDetector(view.context, SingleTapConfirm())

view.setOnTouchListener { _view, event ->
if(gestureDetector.onTouchEvent(event)) {
callback.invoke()
}else {
when (event.actionMasked) {
MotionEvent.ACTION_DOWN -> {
dX = _view.x - event.rawX
dY = _view.y - event.rawY

xAnimation.cancel()
yAnimation.cancel()
}
MotionEvent.ACTION_MOVE -> {
view.animate()
.x(event.rawX + dX)
.y(event.rawY + dY)
.setDuration(0)
.start()
}
MotionEvent.ACTION_UP -> {
xAnimation.start()
yAnimation.start()
}
}
}
true
}
}
private class SingleTapConfirm : GestureDetector.SimpleOnGestureListener() {
override fun onSingleTapUp(event: MotionEvent): Boolean {
return true
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
mainActivityViewModel = ViewModelProvider(this).get(MainActivityViewModel::class.java)
mainActivityViewModel.setUpAnimation(view = binding.floatingBtn, callback = {
Toast.makeText(this, "Button clicked !!", Toast.LENGTH_LONG).show()
})
}

Treat from my side :

Android Developer @BluSmart