Урок 11. Работа с RecyclerView на примере TweetsRecyclerView
Добавление разделителя информации о пользователе и списка твитов
Вначале вспомним, как должен выглядеть блок информации о пользователе.
Видим, что нам необходимо добавить View серого цвета после информации о пользователе и разместить под ней список RecyclerView . Мы бы могли поместить эту View просто под последним TextView , но View должна занимать всю ширину. Это трудно сделать потому что у RelativeLayout установлено свойство padding , которое делает отступ перед всеми вложенными элементами. Поэтому сделаем LinearLayout корневым элементом нашего layout , в свою очередь у него будет 3 вложенных элемента:
- RelativeLayout с информацией о пользователе. Тот контейнер, который до этого момента являлся корневым.
- View – разделитель информации.
- RecyclerView – список твитов, который мы добавим чуть позже в этом уроке.
Наш layout после этих преобразований выглядит так:
Наш RelativeLayout практически не изменился. Единственное что мы изменили – поменяли значения атрибута android:layout_height c match_parent на wrap_content . Это необходимо, чтобы нашему разделителю хватило места на экране.
Мы просто сделали корневым элементом LinearLayout и добавили нашу View в качестве элемента, который находится под RelativeLayout .
Знакомство с элементом RecyclerView
RecyclerView (рус. переиспользующий компонент) представляет из себя компонент для отображения элементов списка. Как раз он и нужен нам для того, чтобы отобразить список твитов пользователя.
Напомним, что в 7 уроке мы уже создали с вами файл tweet_item_view . В этом занятии мы просто свяжем его с java кодом, чтобы динамически добавлять во View информацию.
Данная тема довольна сложна для понимая с первого раза, поэтому помимо пошагового разбора работы с RecyclerView в данном уроке рекомендуем ознакомиться с полезными материалами прикреплёнными в завершении урока для закрепления материала.
Вначале надо добавить строку implementation 'com.android.support:recyclerview-v7:26.1.0' в файл build.gradle , чтобы иметь доступ в коде к RecyclerView .
Далее добавим наш RecyclerView в layout файл. Не будем добавлять никаких зависимостей от соседних элементов, т.к. его родителем является LinearLayout . Просто поместим его после нашей View разделителя. И свяжем его с элементом в java коде.
В Activity мы создали отдельный метод initRecyclerView() , который вызываем в методе onCreate() .
Для работы с RecyclerView необходим LayoutManager , а также Adapter . LayoutManager отвечает за форму отображения элементов: обычная линейная, в виде сетки, в виде шахматной сетки. В данном случае нам нужен обычный линейный список, поэтому используем LinearLayoutManager :
Если вы сейчас запустите приложение, то увидите, что ничего визуально не изменилось. Это потому что список пуст. Чтобы в нём была какая-то информация его надо заполнить элементами. Для этого нам понадобится класс Adapter (переходник) – класс, который отвечает за связь элементов java кода с View -компонентами. Т.е. получая набор java объектов, мы должны подать его на вход в адаптер, который преобразует его уже в набор View -компонентов, которые и использует в дальнейшем RecyclerView .
Давайте создадим новый пакет adapter , где и разместим класс TweetAdapter . Вспоминаем, что надо нажать по пакету colibri.dev.com.colibritweet правой кнопкой и выбрать New -> Package . После этого вводим имя adapter .
После этого в этом пакете создаём новый java класс ( New -> Java class ), который называем TweetAdapter . Выглядеть результат должен так:
Работа с TweetAdapter
Adapter , используемый при работе с RecyclerView обязан определить объект типа ViewHolder , который предоставляет доступ ко всем View -компонентам в каждой строке списка. Давайте модифицируем наш адаптер, добавив необходимые для RecyclerView зависимости:
Теперь, когда мы определили базовый адаптер и ViewHolder , нам нужно заполнить наш адаптер. Сначала давайте создадим переменную для списка твитов:
Для того, чтобы работа с нашим адаптером была гибкой, нам нужны какие-то рычаги для управления наполнением адаптера. Давайте добавим два метода для этого. Один будет служить для наполнения коллекции, другой – для очистки, если нам понадобится обновить информацию на экране:
Оба этих метода содержат в себе вызов одного и того же метода notifyDataSetChanged() . Он предназначен для того, чтобы дать адаптеру знать, что список элементов изменился и ему нужно позаботиться о том, чтобы перерисовать элементы на экране.
Каждый адаптер должен реализовывать 3 главных метода:
- onCreateViewHolder(ViewGroup parent, int viewType) – метод вызывается для создания объекта ViewHolder , в конструктор которого необходимо передать созданный View -компонент, с которым в дальнейшем будут связываться java объекты. Метод вызывается без нашего личного вмешательства, т.к. RecyclerView в себе инкапсулирует логику переиспользования элементов;
- onBindViewHolder(TweetViewHolder holder, int position) – этот метод отвечает за связь java объекта и View . Метод также вызывается без нашего участия. Он будет вызываться чаще, чем метод onCreateViewHolder из-за того, что View компоненты будут переиспользоваться и в один и тот же визуальный элемент в процессе жизнедеятельности списка будут устанавливаться разные данные;
- getItemCount() – сообщает количество элементов в списке.
Давайте реализуем их:
Отлично, теперь осталось добавить код, который будет заполнять данными наши View -компоненты в TweetViewHolder :
Всё, что делает данный код – используя ранее созданные View -компоненты, он заполняет их новыми данными. Таким образом происходит переиспользование View -компонентов без лишнего создания новых. Именно с этим связана следующая строка кода метода bind нашего TweetViewHolder :
Если бы переиспользования элементов не было, то достаточно было бы написать строку:
Но из-за того, что наши элементы переиспользуются, то один раз скрыв изображение на одном элементе, оно будет скрыто на нём и при повторном использовании. Даже в том случае, если оно должно быть показано. Это нас не устраивает.
Также давайте подробнее остановимся на методе форматирования даты, использующегося при заполнении данными нашего компонента:
Т.к. различные сервисы предоставляют дату в самых различных форматах, то возникает необходимость создавать механизмы, которые бы преобразовывали дату одного формата в формат необходимый нам.
Константа TWITTER_RESPONSE_FORMAT представляет собой формат, в котором мы получим дату от Twitter-сервиса. Такую дату отображать пользователю было бы странно. Нас больше устроил бы более простой вариант MONTH_DAY_FORMAT , когда мы отображаем только месяц и день.
Метод String getFormattedDate(String rawDate) принимает на вход дату в формате, в котором мы получим её от сервиса. На выходе же мы получаем готовую к использованию строку даты.
Для начала нам необходимо из строк шаблонов создать java объекты SimpleDateFormat , при помощи которых мы и будем производить преобразования. Т.к. напрямую строку даты одного формата в строку другого формата преобразовать мы не можем, то нам нужен переходный элемент:
Данной операцией мы преобразовали строку, пришедшую нам от сервиса Twitter, в соответствии с шаблоном к обычном java объекту Date , которым теперь можно манипулировать каким угодно образом. Нам остаётся просто отформатировать вновь созданный объект даты в соответствии с необходимым нам шаблоном:
Добавление TweetAdapter к списку RecyclerView
Вот и всё, теперь осталось только сказать нашему RecyclerView , что за наполнение элементов в нём отвечает созданный нами адаптер. Достаточно добавить следующий код в UserInfoActivity :
Теперь давайте убедимся, что наш список действительно работает. Для этого:
- создадим метод Collection<Tweet> getTweets() , который будет возвращать нам коллекцию объектов-заглушек.
- создадим метод loadTweets() , где вызовём метод getTweets() и отобразим результат в экземпляре TweetAdapter .
Теперь давайте запустим наше приложение и посмотрим, что получилось.
Отличная работа. Подытожим всё вышесказанное схемой, которая используется в официальной документации для отображения принципа работы c RecyclerView :
В этой схеме чётко разграничены 3 слоя: View -слой – это непосредственно сам RecyclerView , а также его «помощник» LayoutManager , который отвечает за форму отображения элементов. Dataset – это данные, которые необходимо отобразить в списке. Могут быть представлены в любом виде: как в нашем случае – методом-заглушкой или реально запрашиваемыми с сервера данными, как мы это сделаем в следующих уроках. * Adapter представляет собой натуральный переходник между сырыми данными и пользовательским интерфейсом, готовым к их отображению.
Вот и всё! Сегодня мы с вами приблизились к пониманию очень непростой темы.
По итогу этого урока мы полностью подготовили наш экран списка твитов для их просмотра. Осталось только получить их.
На всякий случай отобразим полный листинг кода UserInfoActivity и TweetAdapter :