Tips for professional use RecyclerView. Part 2
- Transfer
Continuing the previous article in this I will tell you about ItemDecoration
and ItemAnimator
and try to explain how it works in RecyclerView
the example of a simple application that is available on Github .
1. ItemDecoration
ItemDecoration
used to decorate list items in RecyclerView
.
With the help ItemDecoration
you can add separators between view
-components, align them or split them in equal intervals. To add a simple separator between view
-components, use the class DividerItemDecoration
that can be found in the support library version 25.1.0 and higher. The following code snippet demonstrates its implementation:
mDividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(),
mLayoutManager.getOrientation());
recyclerView.addItemDecoration(mDividerItemDecoration);
The best way to create your own separator is to extend the class RecyclerView.ItemDecoration
. In the sample application, I used GridLayoutManager
and applied CharacterItemDecoration
to RecyclerView
:
recyclerView.addItemDecoration(new CharacterItemDecoration(50));
Here it CharacterItemDecoration
sets the offset ( English offset) to 50 pixels in its constructor and overrides getItemOffsets(...)
. Inside the method, getItemOffsets()
each field outRects
determines the number of pixels that must be set for each view
component, like internal and external indents. Since I used GridLayoutManager
and wanted to set equal distances between the grid elements, I set the right padding to 25 pixels (i.e. offset / 2) for each even element and the left padding to 25 pixels for each odd element, while keeping the top padding the same for all items.
2. ItemAnimator
ItemAnimator
Used to animate elements or view
components inside RecyclerView
.
Let's make our app Instagram-like by expanding DefaultItemAnimator
and redefining several methods.
publicbooleancanReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder){
returntrue;
}
The method canReuseUpdatedViewHolder(...)
determines whether the same will ViewHolder
be used for animation if the data of this element changes. If it returns false
, then both ViewHolders
old and updated are passed to the method 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) {
returnnew CharacterItemHolderInfo((String) payload);
}
}
}
returnsuper.recordPreLayoutInformation(state, viewHolder, changeFlags, payloads);
}
publicstaticclassCharacterItemHolderInfoextendsItemHolderInfo{
public String updateAction;
publicCharacterItemHolderInfo(String updateAction){
this.updateAction = updateAction;
}
}
RecyclerView
calls the method recordPreLayoutInformation(...)
before drawing starts layout
. ItemAnimator
must write down the necessary information about the view
component before it is overwritten, moved or deleted. The data returned by this method will be transferred to the corresponding animation method (in our case it is animateChange(...)
).
@OverridepublicbooleananimateChange(@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);
}
}
returnfalse;
}
privatevoidanimatePhotoLike(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
calls a method animateChange(...)
when the adapter element is present at the same time before and after drawing after calling the method notifyItemChanged(int)
. This method can also be used when calling notifyDataSetChanged()
, if the adapter uses stable identifiers. This is necessary in order to RecyclerView
be able to reuse the view
components in the same ViewHolders
. Note that this method takes as arguments: (ViewHolder oldHolder, ViewHolder newHolder, ItemHolderInfo preInfo, ItemHolderInfo postInfo) . As we reuse ViewHolder
, both oldHolder and newHolder are the same.
Whenever a user double-clicks on any item, the following method is called:
notifyItemChanged(position, ACTION_LIKE_IMAGE_DOUBLE_CLICKED);
This starts the whole call chain: canReuseUpdatedViewHolder(...)
, recordPreLayoutInformation(...)
and, ultimately, animateChange(...)
in ItemAnimator
which, in turn, animates a member list and a heart icon in the element (for example SFII above).
This is the second part of a series of articles about RecyclerView
. If you missed the first part, then read it here .
A few more good articles on the topic RecyclerView
:
- RecyclerView - More Animations with Less Code using Support Library ListAdapter
- Using SnapHelper in RecyclerView
- File template for RecyclerView Adapter in Kotlin