Советы для профессионального использования RecyclerView. Часть 2

от автора

Советы для профессионального использования RecyclerView. Часть 2

Продолжая предыдущую статью, в этой я расскажу про ItemDecoration и ItemAnimator и постараюсь объяснить принцип их работы в RecyclerView на примере простого приложения, которое доступно на Github.

1. ItemDecoration

ItemDecoration используется для декорирования элементов списка в RecyclerView.

С помощью ItemDecoration вы сможете добавлять разделители между view-компонентам, выравнивать их или разбивать равными промежутками. Чтобы добавить простой разделитель между view-компонентами, воспользуйтесь классом DividerItemDecoration, который можно найти в библиотеке поддержки версии 25.1.0 и выше. Следующий фрагмент кода демонстрирует его реализацию:

mDividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(),              mLayoutManager.getOrientation()); recyclerView.addItemDecoration(mDividerItemDecoration);

Лучший способ создания собственного разделителя — расширение класса RecyclerView.ItemDecoration. В примере приложения я использовал GridLayoutManager и применил CharacterItemDecoration к RecyclerView:

recyclerView.addItemDecoration(new CharacterItemDecoration(50));

Здесь CharacterItemDecoration устанавливает смещение (англ. offset) на 50 пикселей в своём конструкторе и переопределяет getItemOffsets(...). Внутри метода getItemOffsets() каждое поле outRects определяет количество пикселей, которые необходимо установить для каждого view-компонента, подобно внутренним и внешним отступам. Поскольку я использовал GridLayoutManager и хотел настроить равные расстояния между элементами сетки, я установил отступ справа в 25 пикселей (т.е. offset/2) для каждого чётного элемента и отступ слева в 25 пикселей для каждого нечётного элемента, сохраняя при этом верхний отступ одинаковым для всех элементов.

Отступы элементов в сетке

2. ItemAnimator

ItemAnimator используется для анимации элементов или view-компонентов внутри RecyclerView.

Анимация элементов в списке

Давайте сделаем наше приложение «Инстаграмоподобным», расширив DefaultItemAnimator и переопределив несколько методов.

public boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder) {         return true; }

Метод canReuseUpdatedViewHolder(...) определяет, будет ли один и тот же ViewHolder использоваться для анимации, если данные этого элемента изменятся. Если он возвращает false, то оба ViewHolders — старый и обновленный — передаются в метод animateChange(...).

public ItemHolderInfo recordPreLayoutInformation(@NonNull RecyclerView.State state, @NonNull RecyclerView.ViewHolder viewHolder, int changeFlags, @NonNull List<Object> payloads) {     if (changeFlags == FLAG_CHANGED) {         for (Object payload : payloads) {             if (payload instanceof String) {                 return new CharacterItemHolderInfo((String) payload);             }         }     }     return super.recordPreLayoutInformation(state, viewHolder, changeFlags, payloads); }  public static class CharacterItemHolderInfo extends ItemHolderInfo {         public String updateAction;          public CharacterItemHolderInfo(String updateAction) {             this.updateAction = updateAction;         } }

RecyclerView вызывает метод recordPreLayoutInformation(...) до начала отрисовки layout. ItemAnimator должен записывать необходимую информацию о view-компоненте до того, как он будет перезаписан, перемещен или удалён. Данные, возвращаемые этим методом, будут переданы соответствующему методу анимации (в нашем случае это animateChange(...)).

@Override public boolean animateChange(@NonNull RecyclerView.ViewHolder oldHolder,                                  @NonNull RecyclerView.ViewHolder newHolder,                                  @NonNull ItemHolderInfo preInfo,                                  @NonNull ItemHolderInfo postInfo) {    if (preInfo instanceof CharacterItemHolderInfo) {     CharacterItemHolderInfo recipesItemHolderInfo = (CharacterItemHolderInfo) preInfo;     CharacterRVAdapter.CharacterViewHolder holder = (CharacterRVAdapter.CharacterViewHolder) newHolder;        if (CharacterRVAdapter.ACTION_LIKE_IMAGE_DOUBLE_CLICKED.equals(recipesItemHolderInfo.updateAction)) {             animatePhotoLike(holder);           }         }        return false;     }  private void animatePhotoLike(final CharacterRVAdapter.CharacterViewHolder holder) {      holder.likeIV.setVisibility(View.VISIBLE);      holder.likeIV.setScaleY(0.0f);      holder.likeIV.setScaleX(0.0f);       AnimatorSet animatorSet = new AnimatorSet();      ObjectAnimator scaleLikeIcon = ObjectAnimator.ofPropertyValuesHolder               (holder.likeIV, PropertyValuesHolder.ofFloat("scaleX", 0.0f, 2.0f),                PropertyValuesHolder.ofFloat("scaleY", 0.0f, 2.0f), PropertyValuesHolder.ofFloat("alpha", 0.0f, 1.0f, 0.0f));      scaleLikeIcon.setInterpolator(DECELERATE_INTERPOLATOR);      scaleLikeIcon.setDuration(1000);       ObjectAnimator scaleLikeBackground = ObjectAnimator.ofPropertyValuesHolder               (holder.characterCV, PropertyValuesHolder.ofFloat("scaleX", 1.0f, 0.95f, 1.0f),               PropertyValuesHolder.ofFloat("scaleY", 1.0f, 0.95f, 1.0f));      scaleLikeBackground.setInterpolator(DECELERATE_INTERPOLATOR);      scaleLikeBackground.setDuration(600);      animatorSet.playTogether(scaleLikeIcon, scaleLikeBackground);      animatorSet.start(); }

RecyclerView вызывает метод animateChange(...), когда элемент адаптера присутствует одновременно до и после отрисовки после вызова метода notifyItemChanged(int). Этот метод также можно использовать, при вызове notifyDataSetChanged(), если при этом в адаптере используются стабильные идентификаторы. Это необходимо для того, чтобы RecyclerView мог переиспользовать view-компоненты в тех же ViewHolders. Обратите внимание на то, что этот метод принимает в качестве аргументов: (ViewHolder oldHolder, ViewHolder newHolder, ItemHolderInfo preInfo, ItemHolderInfo postInfo). Поскольку мы повторно используем ViewHolder, оба — oldHolder и newHolder — одинаковы.

Всякий раз, когда пользователь дважды кликает на любой элемент, вызывается следующий метод:

notifyItemChanged(position, ACTION_LIKE_IMAGE_DOUBLE_CLICKED);

Это запускает всю цепочку вызовов: canReuseUpdatedViewHolder(...), recordPreLayoutInformation(...) и, в конечном итоге, animateChange(...) в ItemAnimator, который, в свою очередь, анимирует элемент списка и иконку сердечка в этом элементе (пример на гифке выше).

Это вторая часть серии статей про RecyclerView. Если пропустили первую часть, то читайте её здесь.

Ещё несколько хороших статей на тему RecyclerView:

← Советы для профессионального использования RecyclerView. Часть 1


ссылка на оригинал статьи https://habr.com/post/426773/


Комментарии

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

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