Android
ListView
Animation
User Interface
Mobile Development

How to Animate Addition or Removal of Android ListView Rows

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

ListView does not have the built-in item animation support that RecyclerView provides, so row insertion and removal animations usually need to be done manually. The general pattern is: animate the row view itself, update the backing data at the right moment, then notify the adapter. If you remove the data too early, the row disappears before the animation can be seen.

Important Reality Check

If you are building new Android UI, RecyclerView is usually the better choice because it has a proper item animation ecosystem. But if you are maintaining legacy ListView code, you can still create acceptable add and remove animations with a bit of manual control.

The key rule is:

  • animate the visible row view
  • update the adapter data after or during the animation in a controlled way

Basic Adapter Setup

Here is a simple ListView with a mutable list and ArrayAdapter.

kotlin
val items = mutableListOf("One", "Two", "Three")
val adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, items)
listView.adapter = adapter

That is enough for the data side. The animation logic comes next.

Animate Removal Of A Visible Row

For removal, a common pattern is to fade and collapse the row, then remove the item from the data set when the animation ends.

kotlin
1import android.animation.Animator
2import android.animation.AnimatorListenerAdapter
3import android.animation.ValueAnimator
4import android.view.View
5import android.widget.ArrayAdapter
6import android.widget.ListView
7
8fun removeRowWithAnimation(
9    listView: ListView,
10    rowView: View,
11    position: Int,
12    items: MutableList<String>,
13    adapter: ArrayAdapter<String>
14) {
15    val initialHeight = rowView.height
16
17    rowView.animate()
18        .alpha(0f)
19        .setDuration(150)
20        .start()
21
22    val animator = ValueAnimator.ofInt(initialHeight, 0)
23    animator.duration = 200
24    animator.addUpdateListener { valueAnimator ->
25        val layoutParams = rowView.layoutParams
26        layoutParams.height = valueAnimator.animatedValue as Int
27        rowView.layoutParams = layoutParams
28    }
29
30    animator.addListener(object : AnimatorListenerAdapter() {
31        override fun onAnimationEnd(animation: Animator) {
32            items.removeAt(position)
33            adapter.notifyDataSetChanged()
34
35            rowView.alpha = 1f
36            val layoutParams = rowView.layoutParams
37            layoutParams.height = initialHeight
38            rowView.layoutParams = layoutParams
39        }
40    })
41
42    animator.start()
43}

This works best when you already have the visible row view, such as from a button click inside that row.

Animate Addition

For insertion, do the reverse: add the item first so the row exists, then animate the new row when it becomes visible.

kotlin
1fun addItem(items: MutableList<String>, adapter: ArrayAdapter<String>, value: String) {
2    items.add(value)
3    adapter.notifyDataSetChanged()
4}

Then in your adapter's getView, detect newly inserted items and animate their appearance.

kotlin
1view.alpha = 0f
2view.animate()
3    .alpha(1f)
4    .setDuration(200)
5    .start()

You will usually need a small piece of state, such as the last inserted position, so that only the intended row animates.

Why Timing Matters

The biggest source of bad-looking ListView animations is data timing.

If you call:

kotlin
items.removeAt(position)
adapter.notifyDataSetChanged()

before animating the row, the row is gone immediately and there is nothing left to animate. The data change and the view animation must be coordinated.

This is the main difference between hand-rolled ListView animation and the more structured change handling in RecyclerView.

Stable IDs Help

If your adapter supports stable IDs, row transitions are easier to reason about because rows can be identified consistently across updates.

With legacy ListView, stable IDs are not a full item-animation system, but they still reduce UI confusion when data changes rapidly.

When To Prefer RecyclerView

If you need more than a simple fade or collapse, or if rows update frequently, consider migrating. RecyclerView with ListAdapter or DiffUtil provides a much better long-term path for animated list changes.

That does not mean ListView is unusable. It means manual animation effort grows quickly as the UI becomes more dynamic.

Common Pitfalls

  • Removing the item from the data set before the row animation finishes.
  • Animating the wrong row because the visible position and data position got out of sync.
  • Calling notifyDataSetChanged() repeatedly during an animation and causing flicker.
  • Building large, complex animation logic on top of ListView when RecyclerView would be a better fit.
  • Forgetting to reset properties such as alpha and height after reusing a row view.

Summary

  • 'ListView does not provide built-in row insertion and removal animation support like RecyclerView.'
  • For removals, animate the row first and update the data set when the animation ends.
  • For additions, insert the item and animate the new row as it appears.
  • Keep data timing and view timing coordinated or the effect will look broken.
  • If the list behavior is becoming complex, migrate to RecyclerView instead of extending ListView further.

Course illustration
Course illustration

All Rights Reserved.