Bouncy Animation With Android’s SpringAnimation X and Y

Ramsha Khan
The Startup
Published in
4 min readSep 12, 2020

SpringAnimation is “physics-based motion driven by force”. An important part of this animation is a SpringForce that guides the interactivity and motion. It has two properties:

  1. Damping — Reduction in the amplitude of an oscillation
  2. Stiffness — Strength of the Spring

This article will show how to use SpringAnimation for creating draggable views that, when released, bounces back to their original position.

Lets get started!

1. Add the support library

implementation 'androidx.dynamicanimation:dynamicanimation:1.1.0-alpha03'

2. Create a custom view

Create a view extending an AppCompatImageView, or any other choice of view depending on the requirements.

class BounceView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AppCompatImageView(context, attrs, defStyleAttr) {

init {
...
}
}

2.1 Create SpringAnimation instances

Since the end result is to handle the view x and y motion, two SpringAnimation instances need to be created, one for x and another for y.

lateinit var animationX : SpringAnimation
lateinit var animationY : SpringAnimation

2.2 Add variables to save the original position

To tug the view back to its original place, original coordinates need to be saved.

var originalX = 0f
var originalY = 0f

2.3 Use OnGlobalLayoutListener

The view needs to perform certain tasks once it has been laid out. ViewTreeObserver.OnGlobalLayoutListener can be utilised where its onGlobalLayout() callback can perform the tasks below:

  • Initialise originalX and originalX (Step 2.4)
  • Initialise animationX and animationY (Step 2.5)
  • Set up view’sOnTouchListener (Step 2.6)
viewTreeObserver.addOnGlobalLayoutListener(object :
ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
//do tasks here viewTreeObserver.removeOnGlobalLayoutListener(this)
}
})

2.4 Initialise the original coordinates

The placement of a view can only be known when it has been drawn. The code below can be added to the observer attached in Step 2.3.

originalX = x
originalY = y

2.5 Initialise the SpringAnimation

Once the coordinates are known, SpringAnimation created in Step 2.1 can be initialised.

fun createAnimationX() {
animationX = SpringAnimation(this, SpringAnimation.X).apply {
spring = SpringForce().apply {
finalPosition = originalX
stiffness = SpringForce.STIFFNESS_MEDIUM
dampingRatio
= SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY
}
}
}
fun createAnimationY() {
animationY = SpringAnimation(this, SpringAnimation.Y).apply {
spring = SpringForce().apply {
finalPosition = originalY
stiffness = SpringForce.STIFFNESS_MEDIUM
dampingRatio
= SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY
}
}
}

SpringForce, as introduced in one of the earlier sections, has been used to specify the stiffness and dampingRatio of the animation by using Android’s predefined settings. The finalPosition refers to the final coordinate the view will animate to.

2.6 Enable dragging

To handle any touch event that will drag the view, a OnTouchListener has to be implemented. It will, for basic dragging, handle two MotionEvents:

a. ACTION_DOWN:

Triggered when the screen is first touched. deltaX and deltaY, that is the space between the view’s coordinate and the touch point’s coordinate, will be calculated here.

b. ACTION_MOVE:

Triggered when the finger is moved across the screen. In order to drag the view with finger, either one of the following approaches can be used:

  • Change view position using LayoutParams
  • Animate view to new x and y
  • Change view position using setX and setY
  • Change view position using translationX and translationY

This tutorial will implement drag using setX and setY .

var deltaX = 0f
var deltaY = 0f

var translateX = 0f
var translateY = 0f
setOnTouchListener(object : View.OnTouchListener {
override fun onTouch(view: View, event: MotionEvent?): Boolean {
when(event?.action) {
MotionEvent.ACTION_DOWN -> {
deltaX = view.x - event.rawX
deltaY = view.y - event.rawY
}

MotionEvent.ACTION_MOVE -> {
translateX = deltaX + event.rawX
translateY = deltaY + event.rawY

view.x = translateX
view.y = translateY
}
}

return true
}
})

2.7 Bounce the View back to it’s original position

Up to this point, the view will move along with the finger/pointer. Upon releasing the view, it will remain in its current position. To bounce the view back to its original coordinates set in Step 2.4, one more MotionEvent needs to be handled:

a. ACTION_UP

Triggered when the gesture/touch ends. In this event, SpringAnimation initialised in Step 2.5 can be started.

MotionEvent.ACTION_UP -> {
animationX.start()
animationY.start()
}

3. Add BounceView to Fragment/Activity

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/containerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">

<com.photobook.springanimationapp.customview.BounceView
android:layout_width="80dp"
android:layout_height="80dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_android" />

</androidx.constraintlayout.widget.ConstraintLayout>

That is it! You can view the complete BounceView class here.

Stay tuned

Stay tuned as my next article will show how to utilise this BounceView for creating a floating view, similar to a chat-head, within an app.

Floating view like a chat-head within Photobook App

--

--