
Continuando o artigo anterior , discutirei sobre ItemDecoration
e ItemAnimator
e tentarei explicar o princípio de seu trabalho no RecyclerView
usando um exemplo de aplicativo simples disponível no Github .
1. ItemDecoration
ItemDecoration
usado para decorar itens da lista em um RecyclerView
.
Com o ItemDecoration
você pode adicionar divisórias entre os componentes da view
, alinhá-los ou dividi-los em intervalos iguais. Para adicionar um separador simples entre os componentes de view
, use a classe DividerItemDecoration
, que pode ser encontrada na biblioteca de suporte versão 25.1.0 e superior. O seguinte fragmento de código demonstra sua implementação:
mDividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(), mLayoutManager.getOrientation()); recyclerView.addItemDecoration(mDividerItemDecoration);
A melhor maneira de criar seu próprio separador é estender a classe RecyclerView.ItemDecoration
. No aplicativo de exemplo, usei o GridLayoutManager
e apliquei CharacterItemDecoration
ao RecyclerView
:
recyclerView.addItemDecoration(new CharacterItemDecoration(50));
Aqui CharacterItemDecoration
define o deslocamento ( eng. Deslocamento) para 50 pixels em seu construtor e substitui getItemOffsets(...)
. Dentro do método getItemOffsets()
, cada campo outRects
determina o número de pixels que devem ser configurados para cada componente de view
, semelhante ao recuo e recuo. Como usei o GridLayoutManager
e desejei definir distâncias iguais entre os elementos da grade, defino o recuo à direita em 25 pixels (isto é, offset / 2) para cada elemento par e o recuo à esquerda em 25 pixels para cada elemento ímpar, mantendo o recuo superior o mesmo para todos os elementos.

2. ItemAnimator
ItemAnimator
usado para animar elementos ou view
componentes dentro de um RecyclerView
.

Vamos tornar nosso aplicativo semelhante ao Instagram estendendo o DefaultItemAnimator
e substituindo vários métodos.
public boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder) { return true; }
O canReuseUpdatedViewHolder(...)
determina se o mesmo ViewHolder
usado para animação se os dados desse elemento forem alterados. Se retornar false
, os dois ViewHolders
- antigos e atualizados - são passados para o 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
chama o recordPreLayoutInformation(...)
para iniciar a renderização do layout
. ItemAnimator
deve registrar as informações necessárias sobre o componente de view
antes de ser substituído, movido ou excluído. Os dados retornados por esse método serão transferidos para o método de animação correspondente (no nosso caso, isso é 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
chama o animateChange(...)
quando o elemento do adaptador está presente antes e depois da renderização após chamar o notifyItemChanged(int)
. Este método também pode ser usado ao chamar notifyDataSetChanged()
, se o adaptador usar identificadores estáveis. Isso é necessário para que o RecyclerView
possa reutilizar os componentes de view
nos mesmos ViewHolders
. Observe que esse método usa como argumentos: (ViewHolder oldHolder, ViewHolder newHolder, ItemHolderInfo preInfo, ItemHolderInfo postInfo) . Como estamos reutilizando o ViewHolder
, oldHolder e newHolder são os mesmos.
Sempre que o usuário clica duas vezes em qualquer item, o seguinte método é chamado:
notifyItemChanged(position, ACTION_LIKE_IMAGE_DOUBLE_CLICKED);
Isso inicia toda a cadeia de chamadas: canReuseUpdatedViewHolder(...)
, recordPreLayoutInformation(...)
e, finalmente, animateChange(...)
no ItemAnimator
, que, por sua vez, anima o item da lista e o ícone de coração nesse elemento ( exemplo no GIF acima).
Esta é a segunda parte de uma série de artigos sobre o RecyclerView
. Se você perdeu a primeira parte, leia aqui .
Mais alguns bons artigos sobre o RecyclerView
:
← Dicas para uso profissional RecyclerView. Parte 1