Animations & Transitions
Animate views with ViewPropertyAnimator, ObjectAnimator, and AnimatorSet. Add fragment transitions, shared element animations, and spring physics.
Animations & Transitions is a free Android Academy lesson on CoddyKit — lesson 4 of 5. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Android Academy learning path, one of 5 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Why Animations Matter
Animations make apps feel alive and responsive. They:
- Provide visual feedback for user actions
- Show how screens relate to each other
- Make data changes less jarring
- Help users understand what changed and why
The key rule: animations should serve the UI, not distract from it. Keep them under 300ms.
ViewPropertyAnimator — Quick Animations
The simplest way to animate a view. Chain calls and call start():
// Fade a view out:
binding.tvMessage
.animate()
.alpha(0f)
.setDuration(300)
.withEndAction { binding.tvMessage.visibility = View.GONE }
.start()
// Slide in from bottom:
binding.cardView.translationY = 200f
binding.cardView.alpha = 0f
binding.cardView
.animate()
.translationY(0f)
.alpha(1f)
.setDuration(250)
.setInterpolator(DecelerateInterpolator())
.start()ObjectAnimator
ObjectAnimator animates any property of any object by name. More flexible than ViewPropertyAnimator:
// Rotate an image:
val rotateAnimator = ObjectAnimator.ofFloat(
binding.ivLogo,
"rotation",
0f, 360f
).apply {
duration = 600
repeatCount = ObjectAnimator.INFINITE
interpolator = LinearInterpolator()
start()
}
// Stop it:
rotateAnimator.cancel()AnimatorSet — Sequential & Parallel
AnimatorSet orchestrates multiple animators to run together or one after another:
val fadeIn = ObjectAnimator.ofFloat(binding.card, "alpha", 0f, 1f).setDuration(300)
val scaleX = ObjectAnimator.ofFloat(binding.card, "scaleX", 0.8f, 1f).setDuration(300)
val scaleY = ObjectAnimator.ofFloat(binding.card, "scaleY", 0.8f, 1f).setDuration(300)
val slideUp = ObjectAnimator.ofFloat(binding.card, "translationY", 100f, 0f).setDuration(300)
AnimatorSet().apply {
// Run fadeIn + scaleX + scaleY together, then slideUp
play(fadeIn).with(scaleX).with(scaleY)
play(slideUp).after(fadeIn)
interpolator = DecelerateInterpolator()
start()
}Interpolators
Interpolators control the rate of change of an animation. Common ones:
LinearInterpolator— constant speedAccelerateInterpolator— starts slow, speeds up (exits)DecelerateInterpolator— starts fast, slows down (entries)AccelerateDecelerateInterpolator— slow → fast → slow (default)OvershootInterpolator— overshoots the target and bounces backBounceInterpolator— bounces at the end
TransitionManager — Layout Changes
Animate layout changes automatically by calling TransitionManager.beginDelayedTransition() before changing visibility or layout properties:
binding.btnToggle.setOnClickListener {
// Tell the framework to animate the next layout change
TransitionManager.beginDelayedTransition(
binding.root,
ChangeBounds().apply { duration = 300 }
)
// Now change visibility/size — Android animates the difference automatically
binding.expandedSection.visibility =
if (binding.expandedSection.isVisible) View.GONE else View.VISIBLE
}Fragment Transitions
Add enter/exit animations to fragment transactions:
supportFragmentManager.beginTransaction()
.setCustomAnimations(
R.anim.slide_in_right, // enterAnim
R.anim.slide_out_left, // exitAnim
R.anim.slide_in_left, // popEnterAnim
R.anim.slide_out_right // popExitAnim
)
.replace(R.id.container, DetailFragment())
.addToBackStack(null)
.commit()
// Or use MaterialSharedAxis for a modern, Material Motion feel:
val forward = MaterialSharedAxis(MaterialSharedAxis.X, true)
exitTransition = forward
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)Shared Element Transitions
Share a view between two screens so it animates from one position to another (e.g., a thumbnail that expands into a full image):
// In ListFragment:
binding.ivThumbnail.transitionName = "hero_image_${item.id}"
val extras = FragmentNavigatorExtras(
binding.ivThumbnail to "hero_image_${item.id}"
)
findNavController().navigate(R.id.action_list_to_detail, null, null, extras)
// In DetailFragment:
sharedElementEnterTransition = TransitionInflater
.from(requireContext())
.inflateTransition(android.R.transition.move)
// ImageView in detail layout must have the same transitionNameSpring Animation
Spring animations feel more natural than linear or decelerate because they simulate physical spring physics:
implementation 'androidx.dynamicanimation:dynamicanimation:1.0.0'
// Spring animation on a button press:
val spring = SpringAnimation(binding.fabAdd, DynamicAnimation.SCALE_X, 1f).apply {
spring = SpringForce(1f).apply {
stiffness = SpringForce.STIFFNESS_MEDIUM
dampingRatio = SpringForce.DAMPING_RATIO_LOW_BOUNCY
}
}
binding.fabAdd.setOnClickListener {
binding.fabAdd.scaleX = 0.8f
spring.start()
}Lottie — Complex Animations
Lottie plays After Effects animations exported as JSON. Perfect for loading spinners, onboarding, and empty states:
// app/build.gradle:
// implementation 'com.airbnb.android:lottie:6.4.0'
<!-- In layout XML -->
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/lottieView"
android:layout_width="200dp"
android:layout_height="200dp"
app:lottie_rawRes="@raw/loading_animation"
app:lottie_autoPlay="true"
app:lottie_loop="true" />
// Control in code:
binding.lottieView.playAnimation()
binding.lottieView.pauseAnimation()Animation Best Practices
Guidelines for smooth, accessible animations:
- Duration 150–300ms for most UI animations
- Always respect
AnimatorUtils.areAnimatorsEnabled()orSettings.System.ANIMATOR_DURATION_SCALE - Users with vestibular disorders may disable animations — check
Reduce Motionaccessibility setting - Don't animate things that don't need it — less is more
- Test on low-end devices (animations can cause frame drops)
Quick Check
Which interpolator is best for an element entering the screen to feel like it decelerates and settles?
Recap: Animations & Transitions
Give your app life with smooth animations:
view.animate()— quick alpha, translation, scale, rotationObjectAnimator— any property, fine-grained controlAnimatorSet— orchestrate multiple animators (with/after)TransitionManager.beginDelayedTransition()— auto-animate layout changes- Shared element transitions — seamless cross-screen continuity
- Lottie — complex JSON animations from After Effects
Next: Bottom Navigation and Tabs for multi-section apps.
Frequently asked questions
Is the “Animations & Transitions” lesson free?
Yes — the full text of “Animations & Transitions” is free to read here on the web, and the Android Academy course includes 5 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Android Academy course, upgrade to CoddyKit PRO.
What will I learn in “Animations & Transitions”?
Animate views with ViewPropertyAnimator, ObjectAnimator, and AnimatorSet. Add fragment transitions, shared element animations, and spring physics. You practise Android Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Android Academy?
No prior experience is required. Android Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 5, so you can start here or from the beginning and move at your own pace.
How long does the “Animations & Transitions” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Android Academy lesson?
Yes. Every Android Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Material Design Basics
- Themes & Styles
- Custom Views
- Animations & Transitions
- Bottom Navigation & Tabs