Приветствую на очередном уроке, посвященном работе с RecyclerView, работе со списками. И в этом видео мы обсудим декораторы. И прежде чем я объясню, что такое декораторы, давайте посмотрим на наш список. Мы видим, что список есть, его можно обновлять, на элементы можно тыкать, вызывать всякого рода звонки. Но, при всем при этом, этот список не выйдет как список. Он какой-то безликий. Если человек заранее не знает, что это список, то он даже не поймет, что это список. Давайте мы сделаем его чуть более красивее, добавив разделительную линию между элементами списка. Добавить разделительную линию мы можем двумя способами: легким и через декораторы. И я сначала покажу легкий способ. Легкий способ заключается в том, что мы добавляем разделительную линию на сам элемент списка. То есть мы открываем наш элемент списка li_mock (list item mock) и просто добавляем разделительную линию на саму разметку. View, width, ширина — match_parent. Высота (разделители делают небольшими), высота 1 dp. Закрываем. Background color сделаем темно-серым. Решетка, пусть будет четыре, четыре, четыре. Видим разделитель на экране. У TextView второй добавим padding, точнее, marginBottom 8 dp. Чтобы разделитель не был привязан к элементу. Дальше убираем padding у Linear Layout, у самого холста. И добавляем margin-ы к TextView. MarginTop 8 dp. MarginStart 8 dp. И у второго элемента тоже margin Start 8 dp. Так. Получили элемент списка, в самой верстке которого находится разделительная линия. Давайте попробуем запустить приложение и посмотрим, что будет. Обновляем. Теперь этот список больше похож на список. [БЕЗ_ЗВУКА] Давайте теперь рассмотрим работу с Card View. Сделаем наш список еще красивее. Переходим во вкладку design и находим в палитре, в категории AppCompact CardView и просто перетаскиваем на наше дерево компонентов. Я делаю это для того, чтобы зависимость CardView добавилась в проект. На самом деле, CardView не будет добавлена именно в таком виде. Из CardView мы сделаем подложку, холст. Так. Удаляем. И создаем CardView. CardView. Вот так. [БЕЗ_ЗВУКА] height в wrap_Content, width — match_parent, Ctrl+Alt+L, Ctrl+X и Ctrl+V, то есть обернули наш элемент списка в CardView. Зачем? CardView — это компонент из библиотеки поддержки, который делает списки более красивыми. Возможно, вы видели. Сейчас даже покажу. Margin 8 dp. cardElevation 4 dp и удалим наш divider, он нам не нужен. Что еще мы можем сделать? У CardView задать background белый #FFF. А у элемента списка задать background selectableItemBackground из атрибутов. Что из этого произойдет? Что получится? Давайте посмотрим. xmls здесь нам не нужен. Удалим. Alt+Enter и запускаем. Обновляем список. Видим, что наш список теперь в виде карточек. Нажимаем на список, видим красивую анимацию. Слишком много звонков одновременно. Не успевает обрабатывать. Как раз-таки, это следствие того, что курсор loader работает в главном потоке, заторможенность интерфейса. Что мы только что сделали? Я только что показал, как в разметке самого элемента списка добавлять декорацию, то есть как-то изменять ее. Я добавил сначала разделительную линию через ViewDivider, указал размеры, указал фоновый цвет. Затем я обернул элемент списка в карточку, в ней указал margin-ы, что привело к тому, что у карточки появились красивые отступы по бокам, и указал elevation, что добавило тень под нашей карточкой. Дальше я указал цвет у карточки отдельно и цвет у Linear Layout отдельно. Это был простой способ декорирования списков. Сложный способ заключается в добавлении конкретного класса, который наследуется от RecyclerView ItemDecorator, и мы добавляем реализацию этого класса в RecyclerView, что приводит к тому, что наш список преображается. Само создание декораторов — это сложный процесс. Дело в том, что рисование в нем происходит на более низком уровне, чем то, чем мы только что занимались. Поэтому я сейчас скопирую готовый класс декоратора, и мы просто пройдемся по его коду. Я скопировал класс CardDecoration. И прежде чем мы начнем его разбирать, давайте отменим изменения в элементе списка, но отменять мы не будем прямо всё. Давайте уберем только отступы по бокам главной карточки. Я думаю, этого должно быть достаточно, чтобы работа декоратора была заметна. LayoutMargin у карточки убрали. Хорошо. Возвращаемся в CardDecoration и рассмотрим этот класс. Во-первых, CardDecoration extends RecyclerView.ItemDecoration. То есть мы собираемся добавить декорацию в RecyclerView и логично предположить, что RecyclerView должен знать про этот класс. Давайте, кстати, это сделаем прямо сейчас. Переходим в фрагмент с Recycler. Находим объявление Recycler. Пишем mRecycler. От addItemDecoration new CardDecoration. Точка с запятой. Все, возвращаемся в наш класс, что мы здесь видим в переменных? Видим два поля типа Paint, типа краска: желтая и красная. Соответственно, в конструкторе у нас идет настройка этих объектов, задается стиль, fill, что значит «полностью закрашен». Задается сглаживание, задается цвет. И то же самое для красной краски. Дальше у нас есть три метода: onDraw, onDrawOver и getItemOffsets. onDraw и onDrawOver пока что закомментированы, их разберем чуть-чуть попозже. Разберем сначала getItemOffsets. Это низкоуровневый код, суть которого заключается в том, что прямоугольник, который представляет собой элемент списка, получает Offsets, то есть отступы. То есть то же самое, как если бы мы задавали margin прямо в самом элементе списка, но при этом чуть-чуть низкоуровнево. R.diman.li_margin ссылается на ресурс, это всего лишь 8 dp. Так, возвращаемся. И так как здесь измерение идет в пикселях, я беру не просто diman, я беру не просто dp, я беру именно dp, переведенные в пиксели getDimensionPixelOffset. И считаю левый край прямоугольника outRectangle — вот он. Правый, верхний, нижний. Сейчас запускаем заново. Запускаем, и сейчас должна сработать декорация, которую мы добавляли в программу. Студия ругается, требует пересборки проекта, скорее всего, из-за того, что я копировал классы. Давайте сделаем rebuild и запускаем. [БЕЗ_ЗВУКА] Обновляем. Да, видим картину. Изменения незаметны, но они все-таки есть. Если приглядеться, то отступы между элементами списка чуть меньше, чем были до этого. При этом если сейчас убрать декорацию, закомментируем эту строку, запустим, то мы получим просто список. А там большая карточка — это сейчас без декорации. Возвращаем декорацию. [БЕЗ_ЗВУКА] Получаем отступы. То есть для каждого элемента был высчитан свой отступ и применен. Так, вернемся к коду декорации и рассмотрим два других метода: onDraw и onDrawOver. Раскомментим. onDraw вызывается перед добавлением элемента списка в recycler. То есть сначала выполнится код в декорации в onDraw. На самом деле у ресайклера может быть несколько декораций, и у каждой из них выполнится метод onDraw. Затем сверху добавится элемент списка. После этого выполнится onDrawOver. Сейчас будет на самом деле адов психодел, потому что по коду мы берем границы нижние и верхние и закрашиваем прямоугольник, где хранится элемент списка в желтый либо в красный цвет, в зависимости от четности или нечетности. Щелкаем OK. Обновляем. И видим, что, да, сначала было закрашено поле и потом сверху был добавлен элемент списка. Вот такой вот психодел. Так. Теперь рассмотрим метод onDrawOver. onDrawOver вызывается уже после того, как элемент списка был добавлен. Это приводит к тому, что рисунок будет нанесен на элемент списка, то есть поверх него. Мы рисуем прямоугольник примерно по центру карточки, причем если подложка была желтая, то рисунок сверху будет красным. И наоборот. Давайте запустим и посмотрим. [НЕРАЗБОРЧИВО] Вот, получили: подложка желтая — рисунок сверху красный. Подложка красная — рисунок сверху желтый. Это и есть декораторы. И если посмотреть на этот код, то если кто-то спросит вас, что лучше — декораторы или разделитель на самой разметке, то на первый взгляд ответом является второе. Проще и быстрее и понятнее добавить разделитель на саму разметку, добавить отступы на саму разметку элемента списка. В чем же преимущество декораторов? Преимущество в том, что, во-первых, у вас может быть несколько списков на экране, которым нужно приделать одно и то же изменение, один и тот же декоратор. Например, у вас везде отступы одинаковые, либо сверху закрашивается что-то одинаково, бейджик внизу справа от элемента списка. И чтобы не городить кучу разных элементов списка с одинаковыми разделителями, с одинаковой декорацией, были придуманы декораторы. В этом их преимущество, преимущество в переиспользовании. Другое преимущество, если в самом фрагменте, точнее, в ресайклере я захочу в рантайме удалить элемент списка, то вместе с ним удалится и разделитель, который был на нем. И при свайпах это будет заметно, и при удалении, и при добавлении, в то время как декоратор никуда не денется — он просто изменится. Как-то так. Снова откроем код декоратора. Смотрите: onDraw получает на вход Canvas, то есть сам холст. И это очень низкоуровневый код. Я сейчас не углубляюсь в это, потому что в в одном из следующих курсов, когда мы будем рассматривать material design, анимации и кастомные view-элементы, будет уделено достаточно много внимания низкоуровневому рисованию — рисованию собственных элементов, где тоже вызываются методы onDraw, onMeasure для измерения и тому подобное. Сейчас, когда получите, просто посмотрите, как это работает, попробуйте поковыряться сами, просмотрите код встроенного декоратора — сейчас я вам его покажу. Ресайклер, addItemDecoration, NewDeviderItemDecoration. Посмотрите код встроенного декоратора — он просто рисует разделительную линию, на самом деле, вертикальную либо горизонтальную. И попробуйте — это необязательно, но попробуйте — разобраться, как это работает. Дальше, если у вас приложение маленькое, то никто не будет ругать за то, что вы добавили декоратор в самой разметке, что вы добавили разделитель в самой разметке. Ничего страшного в этом нет. Но если планируется, что у вас будет несколько элементов списка, большое приложение, одинаковые декораторы какие-либо, рисунки поверх элементов списка, те же бейджики или сердечки (кто знает?), то лучше все-таки углубиться в тему декораторов и использовать именно их. В маленьких проектах добавление разделителей или Offsets в самом элементе списка — это нормально, это нестрашно. Но как только речь заходит о серьезных проектах с анимацией, с жесткими изменениями в рантайме, с несколькими списками, то все-таки лучше использовать именно декораторы. Итак, что я хотел еще сказать? На этом уроке работа со списками у нас завершена. Впереди вас ждет несколько тестовых заданий и на ресайклер вью, и на адаптер, и на лоадеры. Что еще, что мы не рассмотрели? Мы не рассмотрели анимацию списков. Оно и понятно, потому что для этого введен специальный курс. Еще мы не рассмотрели гетерогенные списки, то есть это списки, в которых находится несколько различных элементов. несколько вью-холдеров. Будет теория, и будет практическое задание по нему. Дальше, мы не рассмотрели добавление и удаление элементов в рантайме. Тоже будет теория по этому, и тоже будет практическое задание. Так что не рекомендую вам расслабляться. Что ж, тогда до встречи. [БЕЗ_ЗВУКА]