Spring Animations

ANIMATION

Spring Animation with kotlin in Android

A complete guide for physics based animations.

Spring animation Gif

Prerequisite :

  • You should have an idea about Data Binding and View Model.
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 :

  • Animation part
  1. For a spring animation, there is a spring (property) which we need to define.
  2. And, In that Spring we need to specify the stiffness and the damping ratio.
  1. Since we have attached an observer to the view, it also overrides the default listener provided by the android framework like onCLickListener. So, to get those working we have to create them of our own.
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 :

  1. If you want to create awesome view with much less code then do check out the links below :

Android Developer

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store