ListView: что это такое, простые списки в Android приложениях

Автор: IT Sectr Опубликовано: 2026-02-23 Время чтения: 8 мин

ListView — базовый компонент Android для отображения вертикальных списков, доступный с API 1 (Android 1.0). ListView создаёт список из элементов с помощью ArrayAdapter и поддерживает кастомные макеты ячеек. Разбираемся, что такое ListView, как его настроить и какие подводные камни скрывает этот старый, но всё ещё используемый компонент. В IT Sectr мы встречаем ListView в legacy-проектах и поддерживаем обратную совместимость с Android 4.4. Для сравнения с современной альтернативой читайте статью о RecyclerView.

Главное

  • ListView — встроенный компонент Android SDK для вертикальных списков, появившийся в Android 1.0.
  • ArrayAdapter — стандартный адаптер для привязки массива данных к ListView через toString() или кастомный макет.
  • SimpleAdapter — адаптер для списков из Map-данных, удобный для простых экранов без ViewModel.
  • onItemClickListener — встроенный обработчик кликов, который не требует дополнительной реализации в адаптере.
  • convertView — механизм переиспользования View в ListView, который разработчик должен реализовать вручную (аналог ViewHolder).

Что такое ListView в Android?

ListView — это View-группа из пакета android.widget, предназначенная для отображения прокручиваемого списка элементов. ListView реализует паттерн AdapterView: данные передаются через адаптер (Adapter), который конвертирует элементы данных во View, отображаемые на экране. ListView существует в Android SDK с первой версии (API 1) и не требует дополнительных библиотек.

Архитектура ListView проще, чем у RecyclerView: LayoutManager встроен (только вертикальная прокрутка), анимация изменений отсутствует, разделители устанавливаются через XML-атрибут android:divider. ListView автоматически добавляет OverScrollView и выделение при фокусе, что упрощает прототипирование. По данным Statista (2025), около 15% Android-приложений в топ-100 Play Store всё ещё содержат ListView — в основном legacy-проекты с минимальной поддержкой.

История и актуальность

ListView был основным компонентом списков в Android с 2008 по 2014 год, когда на Google I/O 2014 представили RecyclerView. Несмотря на статус частично устаревшего (soft-deprecated), ListView остаётся доступным во всех версиях Android и не помечен как @Deprecated в SDK API 35. Если ваш проект поддерживает Android 4.4 (API 19) и ниже, ListView — единственный встроенный выбор, так как RecyclerView требует AndroidX и градл-зависимости.

Типы адаптеров: ArrayAdapter и SimpleAdapter

ArrayAdapter — наиболее популярный адаптер для ListView. Он принимает List<T> и макет для каждого элемента. По умолчанию ArrayAdapter вызывает toString() для каждого объекта и отображает текст в simple_list_item_1 (один TextView). Для кастомного отображения нужно переопределить getView() или передать кастомный макет в конструктор.

kotlin
// ListView с ArrayAdapter (простой текст)
class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private val countries = listOf(
        "Россия", "США", "Германия",
        "Франция", "Япония", "Китай", "Бразилия"
    )

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val adapter = ArrayAdapter<String>(
            this,
            android.R.layout.simple_list_item_1,
            countries
        )
        binding.listView.adapter = adapter

        // Обработка клика
        binding.listView.onItemClickListener =
            AdapterView.OnItemClickListener { _, _, position, _ ->
                Toast.makeText(this, countries[position], Toast.LENGTH_SHORT).show()
            }
    }
}

SimpleAdapter предназначен для списков из Map-данных. Он принимает List<Map<String, Any>> и массив from/to для связывания ключей Map с ID виджетов в макете. SimpleAdapter удобен для быстрых прототипов, когда не нужен отдельный класс адаптера. Недостаток — отсутствие type-safe типизации и сложность с кастомной логикой.

xml
<!-- Разметка с ListView -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:divider="@color/divider"
        android:dividerHeight="1px"
        android:scrollbars="vertical" />
</LinearLayout>

Создание кастомного адаптера с ViewHolder

Кастомный адаптер для ListView переопределяет метод getView(), который RecyclerView разбивает на onCreateViewHolder + onBindViewHolder. В ListView оба шага совмещены, поэтому разработчик должен самостоятельно реализовать паттерн ViewHolder через convertView (переиспользуемое View) и тег setTag().

kotlin
// Модель данных
data class Product(
    name: String,
    price: String,
    imageResId: Int
)

// Кастомный адаптер с ViewHolder
class ProductAdapter(
    context: Context,
    private val products: List<Product>
) : ArrayAdapter<Product>(context, 0, products) {

    // ViewHolder для кэширования
    private class ViewHolder(view: View) {
        val nameText: TextView = view.findViewById(R.id.tvName)
        val priceText: TextView = view.findViewById(R.id.tvPrice)
        val imageView: ImageView = view.findViewById(R.id.ivProduct)
    }

    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
        val view = convertView ?: LayoutInflater.from(context)
            .inflate(R.layout.item_product, parent, false)
            .also { it.tag = ViewHolder(it) }

        val holder = view.tag as ViewHolder
        val product = getItem(position)

        holder.nameText.text = product.name
        holder.priceText.text = product.price
        holder.imageView.setImageResource(product.imageResId)

        return view
    }
}

Ключевое отличие от RecyclerView: в ListView метод getView() вызывается для каждого появления элемента на экране. Если не использовать convertView и ViewHolder, каждый скролл создаёт новый объект View, что приводит к утечке памяти и лагам на списках из 50+ элементов. ViewHolder с view.tag — обязательная оптимизация для ListView с кастомными макетами.

Ограничения ListView: почему Google рекомендует RecyclerView

ListView имеет четыре архитектурных ограничения, которые Google исправила в RecyclerView: (1) только вертикальная ориентация — нет сеток, горизонтальных списков или каскадов; (2) отсутствие точечных уведомлений — любое изменение данных вызывает полную перерисовку через notifyDataSetChanged(), что создаёт подёргивания; (3) нет встроенной анимации добавления/удаления элементов; (4) нет разделения обязанностей — LayoutManager встроен, ItemAnimator отсутствует.

xml
<!-- item_product.xml — макет элемента ListView -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="12dp">

    <ImageView
        android:id="@+id/ivProduct"
        android:layout_width="56dp"
        android:layout_height="56dp"
        android:scaleType="centerCrop" />

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:orientation="vertical"
        android:paddingStart="12dp">

        <TextView
            android:id="@+id/tvName"
            android:textSize="16sp"
            android:textStyle="bold" />

        <TextView
            android:id="@+id/tvPrice"
            android:textSize="14sp"
            android:textColor="@color/price" />
    </LinearLayout>
</LinearLayout>

Практические последствия: на списках до 20 элементов ListView работает так же быстро, как RecyclerView. При 100+ элементах разница становится заметной: ListView начинает проседать по FPS из-за отсутствия дифф-расчётов и принудительного ViewHolder. Для списков с частым обновлением (чат, лента, логи) ListView не подходит — используйте RecyclerView с Paging 3. В IT Sectr мы мигрируем ListView на RecyclerView при любых работах над legacy-экранами с более чем 50 элементами.

Настройка ListView через XML-атрибуты

ListView предоставляет набор XML-атрибутов для быстрой настройки без кода: android:divider (цвет/рисунок разделителя), android:dividerHeight (толщина), android:scrollbars (полосы прокрутки), android:fastScrollEnabled (быстрая прокрутка по хендлеру), android:choiceMode (режим выбора: singleChoice, multipleChoice). Атрибут android:entries позволяет заполнить список из массива ресурсов (string-array) без адаптера.

АтрибутОписаниеПример значения
android:dividerЦвет или drawable разделителя@color/gray_light
android:dividerHeightТолщина разделителя в px/dp1dp
android:entriesМассив строк из ресурсов@array/countries
android:choiceModeРежим выбора элементаsingleChoice
android:fastScrollEnabledБыстрая прокруткаtrue
android:overlapAnchorAnchor для всплывающих списков (API 21+)true

Режим выбора: choiceMode="singleChoice" включает RadioButton-стиль, при котором выбранный элемент подсвечивается. choiceMode="multipleChoice" — чекбоксы для нескольких элементов. Данные выбора доступны через listView.checkedItemPosition или listView.checkedItemIds. Важно: при choiceMode ListView использует StateListDrawable для фона — установите собственный селектор, если стандартный синий не подходит под дизайн.

ListView vs RecyclerView: сравнение возможностей

Выбор между ListView и RecyclerView зависит от требований проекта. RecyclerView — современный стандарт, но ListView может быть оправдан для простых экранов с минимальными затратами на поддержку старого кода. Сравнение по всем ключевым параметрам:

ПараметрListViewRecyclerView
Размер библиотекиВстроен в SDK (≈60 КБ)AndroidX (≈300 КБ с зависимостями)
Минимальная версияAPI 1API 14 (через AndroidX)
ОриентацияТолько вертикальнаяВертикальная, горизонтальная, сетка, каскад
ViewHolderРучная реализацияОбязателен по архитектуре
АнимацияТолько fade-переходыDefaultItemAnimator (add, remove, move, change)
Точечные обновленияНет (только notifyDataSetChanged)notifyItemInserted/Removed/Changed
Click listeneronItemClickListener (встроен)Через ViewHolder (нет встроенного)
ПроизводительностьДостаточна для списков до 50 элементовСтабильные 60 FPS для 1000+ элементов

Вывод: для новых проектов используйте RecyclerView. ListView оставьте только в legacy-коде, где миграция неоправданно дорога. Согласно гайдлайнам Material Design 3 (2026), списки любой сложности должны использовать RecyclerView для соответствия современным стандартам производительности и доступности.

Часто задаваемые вопросы

ListView устарел или нет?

ListView не помечен как @Deprecated в Android SDK API 35, но Google не рекомендует его использовать в новых проектах (soft-deprecated). Компонент остаётся работоспособным во всех версиях Android. RecyclerView является официальной заменой с 2014 года. Если вы начинаете новый проект — выбирайте RecyclerView. Если поддерживаете существующий — ListView продолжит работать без изменений.

Как добавить header/footer к ListView?

ListView поддерживает методы addHeaderView(View, Object, boolean) и addFooterView(View, Object, boolean). Header и footer добавляются в общий список элементов, но не участвуют в адаптере — они всегда отображаются в начале и конце списка. Важно: добавляйте header/footer до установки адаптера (setAdapter()), иначе они не будут учтены. После добавления header ID позиций элементов сдвигаются на количество header'ов.

Почему ListView не скроллится внутри ScrollView?

ListView и ScrollView оба обрабатывают вертикальные жесты, что создаёт конфликт: внешний ScrollView перехватывает скролл, и ListView не прокручивается. Решение: (1) не вкладывайте ListView в ScrollView — используйте RecyclerView с NestedScrollingChild, (2) замените ScrollView на NestedScrollView (API 21+), (3) используйте ListView с фиксированной высотой (android:layout_height="...dp") внутри ScrollView, но это сломает адаптивность.

Как очистить ListView и обновить данные?

Очистка данных в ListView зависит от типа адаптера. Для ArrayAdapter: adapter.clear() и adapter.addAll(newList) с последующим adapter.notifyDataSetChanged(). Для кастомного адаптера: обновите список данных и вызовите notifyDataSetChanged(). Внимание: listView.invalidateViews() только перерисовывает View, но не обновляет данные. Всегда меняйте данные через адаптер, а не напрямую.

Как изменить высоту элемента ListView?

Высота элемента ListView задаётся в макете item-разметки. Если макет использует layout_height="wrap_content", высота подстраивается под содержимое. Для фиксированной высоты всех элементов используйте кастомный адаптер, который устанавливает LayoutParams в getView(): view.layoutParams.height = resources.getDimensionPixelSize(R.dimen.itemHeight). Для разнородной высоты используйте RecyclerView с different view types.

Итоги

  • ListView — базовый компонент Android SDK для вертикальных списков, доступный с API 1.
  • ArrayAdapter — простейший адаптер для списка строк; для кастомных макетов нужен переопределённый getView().
  • ViewHolder в ListView реализуется вручную через convertView и view.tag — обязательное условие производительности.
  • ListView поддерживает choiceMode (single/multiple), header/footer, fastScroll и android:entries без адаптера.
  • Ограничения ListView: только вертикальная ориентация, отсутствие анимации, полная перерисовка при любом изменении.
  • ListView не помечен как устаревший, но Google рекомендует RecyclerView для всех новых проектов.
  • Для списков до 50 элементов ListView достаточен; для всего, что сложнее — используйте RecyclerView.

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также