Перейти к содержимому

В чем отличие listview от recyclerview

  • автор:

В чем разница между ListView и RecyclerView?

RecyclerView был создан как улучшенная замена ListView . Основные отличия следующие:

1. RecyclerView переиспользует ячейки списка при скроллинге. Для реализации этой логики используется класс ViewHolder .
В ListView тоже можно реализовать адаптер с ViewHolder , но это необязательно и требует написания бойлерплейт кода.

2. RecyclerView разделяет хранение данных и логику отображения. С RecyclerView легко изменить лэйаут в рантайме, используя различные реализации абстрактного класса LayoutManager .

3. Логика отображения анимации элементов вынесена из RecyclerView в класс ItemAnimator .

В целом RecyclerView имеет более гибкий API, чем ListView , благодаря хорошему дизайну и соответствию принципу единственной ответсвенности.

RecyclerView и ListView. В чем разница?

Поскольку мы Android разработчики, нам не сложно реализовать прокручиваемый список тем способом, который соответствует задаче. Но два наиболее популярных пути — ListView и RecyclerView.

Первый из них прекрасный виджет, который был доступен с API 1. До Android Lollipop мы главным образом использовали этот способ, что было довольно неплохо — API был интуитивно понятен. Но к сожалению мы могли создать только вертикально прокручиваемый список, и чтобы он прокручивался плавно, прриходилось делать все надлежлащим способом. Кроме того, класс ListView довольно тяжелый — у него множество обязанностей. Всякий раз, когда нам требовалось обработать список, т.е. сконфигурировать его, единственный путь сделать это был через объект ListView внутри адаптера.

Сегодня мы используем RecyclerView. Как я упоминал, он появился в Android Lollipop и сразу изменил правила игры. Большинство вещей, которые мы ненавидели в ListView были убраны или изменены в RecyclerView. По умолчанию, мы получали больше возможностей. Создание макетов (LayoutManager) отделено, и мы получили больше свободы для манипуляции с данными, чем манипулирование только внутри адаптера.

Если вы хотите узнать больше об этих механизмах, вы можете глянуть мои статьи, которые рассказывают, как реализовать ListView и RecyclerView

(От переводчика) O RecyclerView можно узнать больше в этой моей заметке https://ziginsider.github.io/RecyclerView/

Итак, опишем наиболее существенные различия между этими двумя механизмами..

ViewHolder

Паттерн ViewHolder помогает сделать прокрутку нашего списка плавной. Он сохраняет ссылки на элементы списка, чтобы затем адаптер мог их переиспользовать. Благодаря этому надувание View элемента (inflating) и вызов дорогого метода findViewById() происходит всего пару раз, а не для каждого элемента списка.

Адаптер RecyclerView в обязательном порядке заставляет нас реализовать надувание элемента списка с помощью двух методов — onCreateViewHolder() и onBindViewHolder().

ListView, с другой стороны, не делает обязательным реализацию ViewHolder. И, если мы не реализуем надувание сами, внутри getView(), мы останемся без эффективной прокрутки в списке.

LayoutManager

LayoutManager берет на себя ответсвенность за макетирование представления элементов. Благодаря этому у RecyclerView нет ответственности за поозиционирование элементов в списке. LayoutManager дает возможность выбирать позиционирование элементов списка относительно друг друга и выбирать способ прокрутки. Например, если нам нужен вертикальный или горизонтальный список, мы выбираем LinearLayoutManager. Для табличного представления более подходит GridLayoutManager.

Раньше, с помощью ListView, мы могли создать список только с вертикальной прокруткой. Это было не удобно. Чтобы реализовать табличное представление, мы должны были использовать другой виджет — GridView.

ItemDecoration

Обязанности ItemDecoration просты в теории — декорировать элементы списка (рисование за ними, перед ними, между ними)- но нетривиальны в реализации. Вы должны наследоваться от класса ItemDecoration и реализовать ваше решение. У RecyclerView по-умолчанию нет разделителей между строк — это соответствует гайдлайнам Material Design. Однако, если нам необходим разделитель, мы можем использовать DividerItemDecoration и добавить его в RecyclerView.

В случае ListView мы можем добавить разделитель прямо в описании layout ListView в файле xml. Но у нас нет гибкого механизма, наподобие ItemDecoration, для реализации разделителя.

ItemAnimator

Последнее, но не менее важное отличие RecyclerView это наличие ItemAnimator. Как мы можем догадаться, он обрабатывает анимации появления или исчезновения элементов списка, или их изменения. По-умолчанию анимации RecyclerView красивые и плавные. Конечно, мы можем изменить эти анимации, реализовав свой ItemAnimator, но это также не очень просто. Чтобы упростить себе жизнь, мы можем расширить класс SimpleItemAnimator и реализовать методы, которые нам нужны (просто добавить анимации во ViewHolder наших View)

Честно говоря, реализация анимации в ListView была болью в заднице. Опять же, нам нужно было выяснять как мы справимся с этой задачей. Ничего хорошего. Я рад, что это закончилось.

Notifying adapter

У нас есть несколько классных механизмов уведомления (notify) в адаптере RecyclerView. Мы все еще можем использовать notifyDataSetChanged(), но, если нам необходимо изменить только один элемент списка, к нашим услугам notifyItemInserted(), notifyItemRemoved() или даже notifyItemChanged() и т.д. Мы должны использовать подходящее уведомление, чтобы сработала надлежащая анимация. Используя ListView мы можем использовать лишь notifyDataSetChanged(), а со всем остальным мы должны были справляться сами.

Вывод

ListView служил нам долгое время. Мы могли покрыть большинство случаев с его помощью. Но потребности пользователей сегодня более сложные. Дизайн списков становится все более и более сложным и ListView уже не справляется. Material Design принес другие изменения — красивее, но сложнее в реализации.

К счастью, когда был представлен RecyclerView, многие проблемы ушли. Этот механизм более эффективен по-умолчанию, анимации проще, макетирование проще и API намного приятнее.

Поэтому, если вы когда-либо задумываетесь какой из них выбрать — ваша первая мысль определенно должна быть о RecyclerView.

Android ListView VS. RecyclerView

Scrollable list is one of the most common feature in modern mobile apps. In Android we have two popular ways to achieve this. We can use either ListView or RecyclerView. Do you know exactly how they work and which one should you choose for your projects?

Patryk Kosieradzki

Android ListView VS. RecyclerView

Scrollable list is one of the most common feature in modern mobile apps. In Android we have two popular ways to achieve this. We can use either ListView or RecyclerView. Do you know exactly how they work and which one should you choose for your projects? Maybe you’re struggling with understanding when to use each of them. Let me help you with that. This article is gonna cover all of these questions and even more!

One last thing before we start

✉️ Android Dev Newsletter

If you enjoy learning about Android like I do and want to stay up to date with the latest, worth reading articles, programming news and much more, consider subscribing to my newsletter��
https://androiddevnews.com

�� Android Talks Podcast

If you’re a Polish speaker and want to listen to what I have to say about Android, architecture, security and other interesting topics, check out my podcast��
https://androidtalks.buzzsprout.com

ListView vs RecyclerView is one of the most common questions you can be asked at an Android Dev Interview, so it is worth to read this article and to test both of them on your own to check out what they can do

Availability

The first key difference between the ListView and RecyclerView is when where they introduced. Well, ListView has been with us since the very beginning (API 1). It was always a way for Android Developers to display scrollable lists. It wasn’t the best solution though, because the list had to create a new view item every time user scrolled and as you know view creation is a very expensive thing to do. The bigger the list, the more resource hungry would the application be. It was a pain in the a** for Android Developers before RecyclerView came out.

NOTE

You could implement ViewHolder in the ListView and reuse the views like in RecyclerView, but it was more like optional thing to do, while in RecyclerView it’s the default way of writing the adapter class.

Developer’s redemption

In 2014, after a long time of waiting, RecyclerView, CardView and Design Support Library were introduced. The idea of RecyclerView was very simple. Instead of creating a new view every time user scrolled to the next positions, just create views once and recycle/reuse them. This was achievable by using ViewHolder. Besides that, there is also LayoutManager, ItemDecoration, ItemAnimator and more. Let’s talk in more detail about them…

So what’s RecyclerView exactly?

RecyclerView is a library for list drawing that essentially provides a fixed-size window to load a large dataset into. When the views get out of scope it recycles the views created before. It is a standalone support library, so in order to use it in your project you have add it separately to your app module’s build.gradle file. As of 2021 it is a part of Jetpack, so all of the info about how to include it in your project can be found here: https://developer.android.com/jetpack/androidx/releases/recyclerview

ViewHolder

A ViewHolder describes an item view and metadata about its place within the RecyclerView. It allows making our list scroll smoothly by storing views references. That’s why it’s called a ViewHolder. By doing this calling findViewById(int id) occurs much less times, instead of calling it for the entire dataset on each view binding. It can be also implemented in ListView, but it’s not mandatory. The RecyclerView’s adapter forces us to use the ViewHolder pattern. The adapter actions are separated to creation — onCreateViewHolder(…) and to updating the view — onBindViewHolder(…). If you implement the adapter for RecyclerView then it is compulsory to provide a ViewHolder. In ListView it’s optional, but not using it causes inefficient scrolling and much higher memory usage.

LayoutManager

LayoutManager is responsible for laying out the row views. It allows us to choose how the views should be displayed and scrolled. Besides that it is telling the ViewHolder when to recycle a child view once it’s out of scope.

When using a ListView, you can only create a vertical-scrolling list, so it’s not really a flexible solution for us. If you wanted to create a grid before RecyclerView was introduced, you had to use another UI component for that — GridView.

Now, that we have the RecyclerView everything is easier. There are 3 main LayoutManagers you can choose from:

LinearLayoutManager — handles vertical and horizontal linear layouts

GridLayoutManager — for managing grid layouts with columns/spans

StaggeredLayoutManager — for handling grids, but each item can have different size

Adapter

The most important thing about the RecyclerView is the Adapter. Like any other adapters it’s responsibility is to bind datasets to views that are going to be displayed in the window. A typical adapter is just an iterator that has getCount() method for knowing about limitations. Usually it depends on the dataset size to create new views for binding. This kind of adapters are also used in ListView or ViewPager for example.

RecyclerView, as mentioned before, uses the ViewHolder pattern by default. In this case the RecyclerView adapter is not creating new views every time, but instead it creates only ViewHolders that hold the inflated views to reuse them later.

NOTE

ViewHolders are created and referenced with specific item viewType (Int), not the position. Because of that it is easier for the adapter to find views to reuse and you can add as many view types as you want, by creating new ViewHolder type classes.

The onBindViewHolder(…) method is used to bind views every time a new list element will appear while scrolling, so it will be called a lot of times during scrolling the list, so make sure it is well written to avoid memory and performance issues (for example adding onClick events, interfaces, etc.)

The last thing you have to take care about is the getItemCount() method. This one will be called several times to know the size limit of the list. It generally returns size of the dataset that is used in the adapter.

ItemDecoration

An ItemDecoration allows the application to add a special drawing and layout offset to specific item views from the adapter’s data set. This can be useful for drawing dividers between items, highlights, visual grouping boundaries and more.

For example, if you want to add divider between RecyclerView items, you can use ItemDecoration to achieve this – DividerItemDecoration to be specific.

ListView does not support ItemDecoration, so you have to figure it out on your own.

ItemAnimator

This class defines the animations that take place on items as changes are made to the adapter. Subclasses of ItemAnimator can be used to implement custom animations for actions on ViewHolder items. The RecyclerView will manage retaining these items while they are being animated, but implementors must call dispatchAnimationFinished(ViewHolder) when a ViewHolder’s animation is finished. In other words, there must be a matching dispatchAnimationFinished(ViewHolder) call for each animateAppearance() , animateChange() animatePersistence() , and animateDisappearance() call.

By default, RecyclerView uses DefaultItemAnimator .

ListView does not support ItemAnimator, so in order to add some animations to it you have to create custom animations in “anim” folder and manually attach it to a specific views.

Notifying Adapter

ListView has one method that you can call in order to tell the adapter (which extends BaseAdapter) that some of the items changed. This is notifyDataSetChanged(). As you can see, this is not very much for us developers. Shouldn’t there be more than just that?

That’s why RecyclerView.Adapter now includes a lot of them, so you can have full control of your items. For example notifyDataSetChanged(), notifyItemChanged(int position), notifyItemInserted(int position) any many more.. Be sure to test it out in different scenarios to fully understand it.

Extras

DiffUtil.Callback

DiffUtil is a utility class that calculates the difference between two lists and outputs a list of update operations that converts the first list into the second one.

It can be used to calculate updates for a RecyclerView Adapter. See ListAdapter and AsyncListDiffer which can simplify the use of DiffUtil on a background thread.

DiffUtil uses Eugene W. Myers’s difference algorithm to calculate the minimal number of updates to convert one list into another. Myers’s algorithm does not handle items that are moved so DiffUtil runs a second pass on the result to detect items that were moved.

The callback helps us to skip manual handling of notifying an adapter. Instead of using different methods for each type of notifications as notifyItemInserted() , notifyItemRemoved() or notifyItemChanged() we just can use this simple method:

DiffUtil, ListAdapter , and AsyncListDiffer require the list to not mutate while in use. This generally means that both the lists themselves and their elements (or at least, the properties of elements used in diffing) should not be modified directly. Instead, new lists should be provided any time content changes. It’s common for lists passed to DiffUtil to share elements that have not mutated, so it is not strictly required to reload all data to use DiffUtil.

ListAdapter

RecyclerView.Adapter base class for presenting List data in a RecyclerView , including computing diffs between Lists on a background thread.

This class is a convenience wrapper around AsyncListDiffer that implements Adapter common default behavior for item access and counting.

While using a LiveData<List> is an easy way to provide data to the adapter, it isn’t required — you can use submitList(List) when new lists are available.

Here’s a quick example:

Summary

ListView has been with us for a long time, but as you can see its deputy RecyclerView is beyond ListView’s capacity. It is more efficient by default, has simpler animations and adapter actions and overall using this new API is quite nice. So if you still wonder what should you choose for your lists in future apps — go for RecyclerView. I hope I helped with understanding the differences between these two. Be sure to test them out on your own and see how different mechanisms work

Sign up for more like this.

How to fix keyboard issues introduced in the latest Jetpack Compose (1.4.0)

How to fix keyboard issues introduced in the latest Jetpack Compose (1.4.0)

Recently (March 22, 2023) Google released a new Jetpack Compose stable version 1.4.0. Our team got excited and wanted to try it out, so we found some time to migrate a few days later. At first, everything was working fine. It seemed like we could just bump the

Top 10 Mobile App Security Risks #1 — Improper Platform Usage on Android

Top 10 Mobile App Security Risks #1 — Improper Platform Usage on Android

The first article in a series dedicated to the OWASP Mobile Top 10 — a comprehensive list of the most common and significant security risks in mobile applications. The aim of this series is to provide an in-depth description of each risk and offer practical examples that demonstrate the potential threat

Mastering Android Dialogs: Don’t follow official Google Guides

Mastering Android Dialogs: Don’t follow official Google Guides

This article explains why Google Guides for Dialogs are bad and what risks you and your apps may face if you follow them.

RecyclerView для начинающего Android-разработчика

Здравствуй, дорогой читатель. Каждый Android-разработчик сталкивался с задачей, в которой необходимо создать какой-то список, для отображения данных. Данная статья поможет новичку разобраться с таким очень важным и интересным компонентом, как RecyclerView.

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

Статья предназначена для новичков, которые хотят разобраться со списками в Android.

Все материалы и исходный код можно найти здесь.

Готовый практический пример, который будет разобран в статье.

ListView или RecyclerView?

Для реализации какого-то прокручиваемого списка у Android разработчика существуют два пути — ListView и RecyclerView.

Первый виджет интуитивно понятен и довольно прост. Но, к сожалению, имеет много недостатков, например, ListView позволяет создать только вертикальный список.

В свою же очередь RecyclerView «из коробки» предоставляет гораздо больше инструментов для кастомизации и оптимизации списка, чем ListView. Если кратко характеризовать RecyclerView, то можно сказать, что это список на стероидах.

RecyclerView работает следующим образом: на экране устройства отображаются видимые элементы списка; при прокрутке списка верхний элемент уходит за пределы экрана и очищается, а после помещается вниз экрана и заполняется новыми данными.

Основные компоненты RecyclerView

Для корректной работы RecyclerView необходимо реализовать следующие компоненты:

RecyclerView , который необходимо добавить в макет нашего Activity;

Adapter , который содержит, обрабатывает и связывает данные со списком;

ViewHolder , который служит для оптимизации ресурсов и является своеобразным контейнером для всех элементов, входящих в список;

ItemDecorator , который позволяет отрисовать весь декор;

ItemAnimator , который отвечает за анимацию элементов при добавлении, редактировании и других операций;

DiffUtil , который служит для оптимизации списка и добавления стандартных анимаций.

Практический пример

В качестве не сложного примера, создадим приложение со списком, в котором будут отображены данные о людях. Каждый человек будет иметь имя, название компании, фотографию и несколько операций над ним (показать уникальный номер, удалить, переместить вверх/вниз, лайкнуть).

Реализация примера будет выполнена на языке Kotlin. Также будут использованы библиотеки Glide и Faker, которые никак не относятся к RecyclerView.

В первую очередь укажем все зависимости, которые будут использованы приложением, в файл сборки build.gradle нашего приложения:

Примечание: в последних версиях AndroidStudio не обязательно подключать библиотеку RecyclerView. Доступен в библиотеке Material.

И необходимо подключить ViewBinding в файле сборки build.gradle нашего приложения:

Также необходимо указать разрешение на доступ в Интернет в файле AndroidManifest.xml (для работы с библиотекой Glide):

После создадим макетные файлы: первый для ActivityMain, который хранит RecyclerView, второй для элемента списка (человек).

После того, как все библиотеки были подключены, все макеты созданы и разрешения получены, необходимо создать данные о людях (в настоящем, боевом примере эти данные будут приходить, например, с сервера, но в нашем случае мы создадим их самостоятельно). Для этого создадим класс PersonService и data-class Person:

В классе PersonService хранится лист пользователей, который мы заполняем в init (initializers blocks), и лист ссылок на фотографии.

После создания классов необходимо класс PersonService сделать Singleton для корректной работы. Для этого создадим класс App, и укажем в нем следующее:

Теперь реализуем адаптер PersonAdapter, который будет обрабатывать наши данные и связывать их со списком.

Данный класс будет реализовать RecyclerView.Adapter , которому нужен ViewHolder. Соответственно необходимо создать PersonViewHolder, который будет реализовывать RecyclerView.ViewHolder и принимать наш binding.

Также PersonAdapter должен иметь данные, с которыми ему предстоит работать. Для этого создадим пустой список и перепишем его сеттер. В итоге получаем:

Но для работы адаптера необходимо переопределить минимум три метода (AndroidStudio подскажет нам).

Метод getItemCount , который будет возвращать количество элементов нашего списка с данными;

Метод onCreateViewHolder , в котором будет происходить создание ViewHolder. Данный метод принимает в себя parent и viewType (используется в том случае, если в списке будут разные типы элементов списка);

Метод onBindViewHolder , в котором будет происходить отрисовка всех элементов в объекте списка (имя человека, компания и т.д.):

После переопределения методов и их реализации получаем:

На этом наш простой адаптер, который будет просто выводить горизонтальный список готов.

Теперь необходимо повесить на наш RecyclerView созданный адаптер и LayoutManager. Для этого в классе MainActivity пропишем следующее:

Запускаем наше приложение на устройстве и получаем список пользователей!

На данном этапе наше приложение просто выводит данные, которые мы можем прокручивать, но взаимодействовать с ними у нас не получится. Исправим это.

В классе PersonService добавим три метода:

likePerson — лайкаем человека;

removePerson — удаляем человека;

movePerson — перемещаем человека (принимает человека и куда надо переместить: «1» — вниз, «-1» — вверх).

После того, как были созданы методы взаимодействия с людьми, в классе PersonService необходимо объявить слушателя:

Так же создадим список слушателей, два метода, которые будут добавлять и удалять слушателей, и один, который будет «регистрировать изменения»:

Метод notifyChanges необходимо обязательно вызвать в методах, в которых происходит модификация данных, то есть в методах likePerson, removePerson и movePerson.

На этом наш сервис людей полностью готов. Перейдем в PersonAdapter, в котором реализуем обработку событий наших людей. Создадим интерфейс PersonActionListener, в котором буду четыре метода:

onPersonGetId — получить уникальный номер выбранного человека;

onPersonLike — человек был лайкнут;

onPersonRemove — удалить человека;

onPersonMove — переместить человека.

Класс PersonAdapter во входные параметры будет принимать наш интерфейс. Также данный класс должен реализовать интерфейс OnClickListener. В итоге сигнатура объявления класса PersonAdaper выглядит следующим образом:

Теперь в классе PersonAdapter в методе onBindViewHolder кладём в tag каждого view, на которую будет происходить нажатие, нужного человека:

Теперь в методе onCreateViewHolder необходимо проинициализировать слушателей при нажатии. В данном примере будет слушатель на нажатие на элемент списка, кнопку more (три точки) и likedImageView (сердце):

Теперь создадим метод showPopupMenu, который будет «рисовать» выпадающее меню с доступными действиями, а именно: удалить пользователя, переместить вверх, переместить вниз:

В методе onClick обработаем нажатия на элементы списка:

На этом наш адаптер готов. Теперь перейдем в MainActivity и, при инициализации нашего адаптера, передадим реализацию интерфейса:

Также добавим слушателя в MainActivity, который будет прослушивать изменения, происходящие в PersonService:

И в методе onCreate добавим этого слушателя:

На этом наша работа выполнена. Запускаем проект и смотрим результат:

Результат.

Результат.

Мы рассмотрели основы RecyclerView. Естественно это далеко не все, что позволяет сделать этот мощный инструмент. Всегда можно добавить DiffUtil, который поможет оптимизировать список, ItemDecorator, для декора наших элементов и т.д.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *