Animation in Android applications is not just an interface decoration, but a powerful tool for improving the user experience. Properly implemented effects help visualize interactions, make navigation more intuitive, and can even increase conversion in commercial projects. However, many developers are still limited to standard transitions fade-in/fade-out, without using the full potential of the platform.
In this article we will look 5 modern approaches to creating animations on Android - from basic XML declarations to advanced techniques using Jetpack Compose i Lottie. You'll learn how to integrate animation into an existing project without losing productivity, which tools to choose for different tasks, and where to look for ready-made solutions for common scenarios. We will pay special attention to optimization: even the most beautiful animation should not slow down the application on weak devices.
1. Basic animation via XML: where to start for a beginner
If you are just starting to work with animation in Android, start with declarative approach via XML files. This method has been supported since the first versions of the platform and is ideal for simple effects: the smooth appearance of elements, rotating buttons or changing colors. All animation files are stored in the folder res/anim/ and connected via Java/Kotlin code.
Main types of XML animations:
- ๐ Tween animation (
<set>,<alpha>,<scale>): changes the properties of an object over time (for example, transparency or size). - ๐ Frame animation (
<animation-list>): sequential display of frames (as in a GIF). - ๐ฏ Property Animation (via
ObjectAnimator): more flexible control over the properties of objects.
Example of a simple tween animation for a button (file res/anim/fade_in.xml):
<set xmlns:android="http://schemas.android.com/apk/res/android"android:duration="1000">
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0" />
</set>
To apply it to an element, use in code:
val animation = AnimationUtils.loadAnimation(this, R.anim.fade_in)
button.startAnimation(animation)
โ ๏ธ Attention: XML animations do not support dynamic changes of parameters at runtime. If you need to interrupt or modify the animation based on a condition, use ObjectAnimator.
2. Property Animation: flexibility and control
For more complex scenarios where required dynamic control animation (for example, reaction to user gestures), suitable Property Animation API. Unlike XML, here you work directly with object properties through classes ObjectAnimator, ValueAnimator and AnimatorSet.
Main advantages:
- ๐ง Interpolators: setting acceleration/deceleration curves (
AccelerateDecelerateInterpolator,BounceInterpolator). - ๐ฑ Cancel and pause: the ability to pause or cancel the animation at any time.
- ๐ Animation chains: sequential or parallel execution via
AnimatorSet.
An example of animation for moving a view across the screen with acceleration:
val view = findViewById<View>(R.id.target_view)val animator = ObjectAnimator.ofFloat(view, "translationX", 0f, 300f).apply {
duration = 1500
interpolator = AccelerateInterpolator(1.5f)
}
animator.start()
To create a chain of animations (for example, first scaling, then rotation):
val scaleX = ObjectAnimator.ofFloat(view, "scaleX", 1f, 2f)val rotate = ObjectAnimator.ofFloat(view, "rotation", 0f, 180f)
AnimatorSet().apply {
play(scaleX).before(rotate) // first scale, then rotation
start()
}
Use ViewPropertyAnimator to simultaneously change several properties of the same type - this optimizes performance by reducing the number of calls invalidate().
3. Lottie: lossless animation from After Effects
If you need complex vector animations (for example, loading screens, morphing illustrations or characters), but do not have time to draw them manually, use Lottie โa library from Airbnbthat renders animations from Adobe After Effects real time.
How it works:
- The designer creates the animation in After Effects and exports it to format
.jsonvia a plugin Bodymovin. - You add a file to the project (folder
assets/) and connect the dependencycom.airbnb.android:lottie. - In the markup, add
<com.airbnb.lottie.LottieAnimationView>and load the animation.
Integration example:
<com.airbnb.lottie.LottieAnimationViewandroid:id="@+id/animationView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:lottie_rawRes="@raw/loading_animation"
app:lottie_loop="true"
app:lottie_autoPlay="true"/>
You can control playback in the code:
val animationView = findViewById<LottieAnimationView>(R.id.animationView)animationView.playAnimation() // start
animationView.pauseAnimation() // pause
animationView.speed = 2f // acceleration by 2 times
| Parameter Lottie | Description | Default value |
|---|---|---|
lottie_rawRes |
JSON file resource ID | โ |
lottie_loop |
Animation looping | false |
lottie_autoPlay |
Automatic start | false |
lottie_speed |
Playback speed | 1.0 |
โ ๏ธ Attention: Lottie animations can significantly increase the size of the APK. Optimize JSON files using the tool LottieFiles Optimizer (remove unnecessary layers and reduce the number of key frames).
Where to download ready-made Lottie animations?
Free animations can be found at resources:
- LottieFiles (more than 100,000 files)
- Lordicon (icons with animation)
- Icons8 (simple animations for UI).
Pay attention to the license - some files require attribution.
4. MotionLayout: complex transitions without code
MotionLayout is an extension ConstraintLayoutthat allows you to create between interface states directly in XML, without writing Java/Kotlin code. The tool is ideal for: complex transition animations between interface states directly in XML, without writing Java/Kotlin code. The tool is ideal for:
- ๐ฑ Adaptive interfaces: smooth changes in the arrangement of elements when the screen is rotated.
- ๐ Complex transitions: for example, opening a panel while simultaneously moving other elements.
- ๐ฏ Animations with gestures: reaction to swipes or clicks.
Main components MotionLayout:
<ConstraintSet>โdescribes the state of elements at the beginning and end of the animation.<Transition>โ defines the transition parameters (duration, interpolator).app:motionSceneโ link to a file with a description of the animation (res/xml/scene_*.xml).
Example of a simple transition (file res/xml/scene_main.xml):
<MotionScene xmlns:app="http://schemas.android.com/apk/res-auto"><ConstraintSet android:id="@+id/start">
<Constraint android:id="@id/button" ... />
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint android:id="@id/button"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
</ConstraintSet>
<Transition
app:constraintSetStart="@id/start"
app:constraintSetEnd="@id/end"
app:duration="1000"/>
</MotionScene>
In the markup we connect MotionLayout:
<androidx.constraintlayout.motion.widget.MotionLayoutandroid:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutDescription="@xml/scene_main">
<Button android:id="@+id/button" ... />
</MotionLayout>
To start the transition by click:
val motionLayout = findViewById<MotionLayout>(R.id.motionLayout)button.setOnClickListener {
motionLayout.transitionToEnd() // transition to the final state
}
Add dependency implementation "androidx.constraintlayout:constraintlayout:2.1.4"
Create a scene file in res/xml/
Describe the initial and final states in <ConstraintSet>
Connect app:layoutDescription in markup
Check animation in Android Studio Layout Editor-->
5. Jetpack Compose: a modern approach to animation
If you are developing a new project or migrating to Jetpack Compose, then for animation you should use the built-in tools of this framework. Compose offers declarative approach, where animation is described as a state of the UI, which simplifies management and testing.
Main APIs for animation in Compose:
- ๐
animate*AsState(animateFloatAsState,animateDpAsState): smooth change of value (for example, alpha channel or size). - ๐ฏ
updateTransition: control several animations at the same time. - ๐ฑ
AnimatedVisibility: appearance/disappearance of elements with effects. - ๐ง
rememberInfiniteTransition: looping animations (for example, button pulsation).
An example of an animation for changing the size of a button when pressed:
var expanded by remember { mutableStateOf(false) }val scale by animateFloatAsState(
targetValue = if (expanded) 1.2f else 1f,
animationSpec = spring(dampingRatio = 0.5f)
)
Button(
onClick = { expanded = !expanded },
modifier = Modifier.scale(scale)
) {
Text("Press me")
}
For complex transitions, use updateTransition:
val transition = updateTransition(expanded, label = "transitionExample")val rectColor by transition.animateColor(label = "colorTransition") { isExpanded ->
if (isExpanded) Color.Green else Color.Red
}
val borderWidth by transition.animateDp(label = "borderTransition") { isExpanded ->
if (isExpanded) 10.dp else 2.dp
}
Box(
Modifier
.background(rectColor)
.border(borderWidth, Color.Black)
.clickable { expanded = !expanded }
)
โ ๏ธ Attention: In Compose, animations are automatically interrupted when the composition leaves the composition (for example, when the screen is rotated). To save the state, use rememberSaveable.
Jetpack Compose allows you to describe animations as part of the UI state, which simplifies synchronization with data and reduces the number of bugs associated with state mismatches.
6. Optimization of animations: how not to slow down the application
Even the most beautiful animation will lose its meaning if there is slow down the interface. To avoid performance problems, follow these rules:
Problems and solutions:
| Problem | Cause | Solution |
|---|---|---|
| Animation lags | Too frequent calls invalidate() |
Use ViewPropertyAnimator or ObjectAnimator instead ValueAnimator. |
| High CPU consumption | Complex calculations in onDraw() |
Move logic to ValueAnimator or use Lottie for vector animations. |
| Jerky animation | Low FPS (less than 60) | Reduce the number of animated properties or use Interpolator for smoothing. |
| Memory leaks | Unreleased animation resources | Always call animator.cancel() to onCleared() or onDestroy(). |
Diagnostic tools:
- ๐ Android Profiler: monitor FPS and CPU usage during animation.
- ๐ Layout Inspector: check view hierarchy - deep nesting can slow down rendering.
- ๐ ๏ธ Overdraw Debugging: enable in the developer settings to find unnecessary redraws.
Critical error: animations on the main thread block the UI. Always use ValueAnimator or ObjectAnimatorwhich automatically optimize execution on the Android animation framework.
7. Ready-made solutions: libraries and tools
You donโt always need to reinvent the wheel. For typical tasks, you can use ready-made libraries that will save time and guarantee stability:
Popular libraries for animations:
- ๐ฌ Lottie (already mentioned): for vector animations from After Effects.
- ๐ Rebound (from Facebook): implementation of physically correct animations (springs, bounces).
- ๐ฑ AndroidViewAnimations: a collection of ready-made effects (shake, ripple, rotation).
- ๐ฏ Shimmer: flickering animation for loading screens ("skeleton" effect).
- ๐ง TransitionEverywhere: backported transitions from new APIs for older versions of Android.
Example of use Shimmer for the loading effect:
<com.facebook.shimmer.ShimmerFrameLayoutandroid:id="@+id/shimmerLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:shimmer_autostart="true">
<include layout="@layout/placeholder_skeleton"/>
</com.facebook.shimmer.ShimmerFrameLayout>
In the code:
val shimmerLayout = findViewById<ShimmerFrameLayout>(R.id.shimmerLayout)shimmerLayout.startShimmer() // start animation
// shimmerLayout.stopShimmer() - stop
โ ๏ธ Attention: Before using third-party libraries, check their relevance. Some projects (for example, NineOldAndroids) are outdated and replaced by native solutions in modern APIs.
FAQ: Frequently asked questions about animations on Android
Is it possible to make animation without XML, only through code?
Yes, all types of animations (except MotionLayout) can be created programmatically. For example, instead of the XML file for ObjectAnimator you can write:
ObjectAnimator.ofFloat(view, "alpha", 0f, 1f).apply {duration = 1000
start()
}
This is convenient if the animation parameters depend on runtime conditions (for example, data from the server).
How to make animation by swipe (like in Tinder)?
For animation by gestures, use a combination of GestureDetector and ObjectAnimator. Example:
- Track the movement of your finger in
onTouchEvent. - Update the position of the view through
view.x = newX. - At the end of the gesture, start a return or delete animation:
val animator = ObjectAnimator.ofFloat(view, "x", view.x, if (swipeRight) 1000f else -1000f)animator.interpolator = AccelerateInterpolator()
animator.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
view.visibility = View.GONE // deletion after animation
}
})
animator.start()
Why does animation slow down on older devices?
On devices with Android 5.0 and below, problems may arise due to:
- Lack of hardware acceleration for some operations.
- Outdated renderer
Canvas. - Unoptimized libraries (for example, old versions) Lottie).
Solutions:
- Disable unnecessary effects for old APIs via
Build.VERSION.SDK_INT. - Use
hardwareAccelerated="true"in the manifest. - Replace complex animations with simple ones (for example, instead of Lottie โ GIF).
How to test animation on different devices?
Use Android Emulator with different configurations:
- Create virtual devices with different versions of Android (from 5.0 to 14).
- Enable the option
Show layout boundsin the developer settings to check the boundaries of elements. - Run
adb shell dumpsys gfxinfo <package>to analyze FPS.
For automated testing, use Espresso s IdlingResource to wait for the end of animations.
Where to find inspiration for animations?
Explore these resources:
- ๐จ Dribbble (Mobile Animation) โa collection of UI animations from designers.
- ๐ฑ Material Design Motion โ Googleโs guideline for animations in Material Design.
- ๐ฌ Airbnb Loading Animations โexamples of loading animations.
- ๐ง UAMP (Universal Android Music Player) โan open project with examples of animations.