Bouncy Animation With Android’s SpringAnimation X and Y
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:
- Damping — Reduction in the amplitude of an oscillation
- 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
andoriginalX
(Step 2.4) - Initialise
animationX
andanimationY
(Step 2.5) - Set up view’s
OnTouchListener
(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
andy
- Change view position using
setX
andsetY
- Change view position using
translationX
andtranslationY
This tutorial will implement drag using setX
and setY
.
var deltaX = 0f
var deltaY = 0f
var translateX = 0f
var translateY = 0fsetOnTouchListener(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.