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 з різними 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 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

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