I have a recyclerView, that contains a list of custom views. When I click a recyclerView item, it is expanded, providing more information. Another click upon this view (or upon another one) closes current one. The default animation, that provides SimpleItemAnimator
, doesn't appear to be suitable. So, I tried to implement animateChange
on my own like this:
public class CustomItemAnimator extends CustomDefaultItemAnimator {
private static final int FADE_IN = 1;
private static final int FADE_OUT = 2;
private ItemHolderInfo getItemHolderInfo(RecyclerView.ViewHolder viewHolder, MoveInfo moveInfo) {
moveInfo.stuff.clear();
ViewGroup content_container = (ViewGroup) viewHolder.itemView.findViewById(R.id.item_content);
for (int i = 0; i < content_container.getChildCount(); i++) {
View view = content_container.getChildAt(i);
if (view.getVisibility() == View.VISIBLE) {
moveInfo.stuff.put(view.getId(), new Pair<>(view.getTop(), view.getBottom()));
}
}
moveInfo.stuff.put(viewHolder.itemView.getId(), new Pair<>(viewHolder.itemView.getTop(), viewHolder.itemView.getBottom()));
moveInfo.stuff.put(content_container.getId(), new Pair<>(content_container.getTop(), content_container.getBottom()));
return moveInfo;
}
@NonNull
@Override
public ItemHolderInfo recordPreLayoutInformation(@NonNull RecyclerView.State state, @NonNull RecyclerView.ViewHolder viewHolder, int changeFlags, @NonNull List<Object> payloads) {
ItemHolderInfo info = super.recordPreLayoutInformation(state, viewHolder, changeFlags, payloads);
return getItemHolderInfo(viewHolder, (MoveInfo) info);
}
@NonNull
@Override
public ItemHolderInfo recordPostLayoutInformation(@NonNull RecyclerView.State state, @NonNull RecyclerView.ViewHolder viewHolder) {
MoveInfo info = (MoveInfo) super.recordPostLayoutInformation(state, viewHolder);
return getItemHolderInfo(viewHolder, info);
}
@Override
public boolean animateChange(@NonNull final RecyclerView.ViewHolder oldHolder, @NonNull final RecyclerView.ViewHolder newHolder, @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
Log.d("ANIMATOR", "animate change called 2");
MoveInfo pre = (MoveInfo) preInfo;
MoveInfo post = (MoveInfo) postInfo;
int fade_style;
final HashMap<Integer, Pair<Integer, Integer>> minMap;
HashMap<Integer, Pair<Integer, Integer>> maxMap;
if (pre.stuff.keySet().size() <= post.stuff.keySet().size()) {
minMap = pre.stuff;
maxMap = post.stuff;
fade_style = FADE_IN;
} else {
minMap = post.stuff;
maxMap = pre.stuff;
fade_style = FADE_OUT;
}
/*Getting the GoneViews*/
HashMap<Integer, Pair<Integer, Integer>> goneViews = new HashMap<>();
for (Integer i : maxMap.keySet()) {
if (minMap.get(i) == null) {
goneViews.put(i, maxMap.get(i));
}
}
List<Animator> animators = new ArrayList<>();
for (Integer i : minMap.keySet()) {
ViewPropertyAnimatorCompat animView = ViewCompat.animate(newHolder.itemView.findViewById(i));
ObjectAnimator viewDeltaTop = ObjectAnimator.ofInt(newHolder.itemView.findViewById(i), "top", pre.stuff.get(i).first, post.stuff.get(i).first);
ObjectAnimator viewDeltaBot = ObjectAnimator.ofInt(newHolder.itemView.findViewById(i), "bottom", pre.stuff.get(i).second, post.stuff.get(i).second);
animators.add(viewDeltaTop);
animators.add(viewDeltaBot);
}
for (Integer i : goneViews.keySet()) {
float from = fade_style == FADE_IN ? 0.0f : 1.0f;
float to = fade_style == FADE_IN ? 1.0f : 0.0f;
Animator viewAlpha = ObjectAnimator.ofFloat(newHolder.itemView.findViewById(i), "alpha", from, to);
animators.add(viewAlpha);
}
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(animators);
animatorSet.setDuration(getChangeDuration());
animatorSet.start();
return true;
}
@Override
public boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, @NonNull List<Object> payloads) {
//to make viewholders reusable
return true;
}
//we store our infos here
private class MoveInfo extends ItemHolderInfo {
HashMap<Integer, Pair<Integer, Integer>> stuff = new HashMap<>();
}
//not implementing this cause cast error in my case
@Override
public ItemHolderInfo obtainHolderInfo() {
return new MoveInfo();
}
}
This works like a charm, except one thing. If I try to scroll, while changes are animated, the animated view is not displayed properly. It looks like as if the animated bottom/top values don't receive any input by the scroll. I rly like the way, the itemAnimator
works, and I would like to make things work at this way. Any tips are appreciated. Probably, I have to examine LinearLayoutManager?