RecyclerView: Animate item resize
Asked Answered
S

2

20

I have a RecyclerView. Each of its items of the recycler view can be either expanded or not. There can be only one item expanded at the same time.

In essence, I'm trying to re-create the history list in lolipop dialier.

I have found that using a LayoutTransition on RecyclerView makes it crash.

But I have not been able to correctly animate the item view change between both states.

I have tried getItemAnimator().setSupportsChangeAnimations(true) in conjunction with notifyItemChanged(getPosition()) but there is two problems:

  • The view is re-created, making the transition quite weird as it fades in above the next item at the same time as the item is moving. The new view that appears is not resizing, it is already at full size.
  • As the view is re-created, the old view is fade out while the new is fade in, which makes the view background color flicker.

I have also tried setting a LayoutTransition on the item view for the duration of the animation but the problem with this approach is that the layout of the RecycleView updates immediately and does not follow the animation.

I have created a small demo project of this issue for both tries. The projects sources are here.

How can I create a smooth transition on item layout change ?

Soucy answered 12/12, 2014 at 14:44 Comment(8)
Sorry that I do not have an answer for you, but I am dealing with the same problem, did you manage to get it working yet? I will keep you posted about what I managed to do!Zygote
I am pretty close, stay tunedZygote
I have not been able to get any closer than the projects I have linked. Looking forward to see your work ;)Soucy
So, this is what I got so far: 1. When you click an item, it stores the clicked position and calls notifyDataSetChanged 2. There are two itemViewTypes, (ExpandedRow & CompactedRow), when deciding between the two all viewTypes will be compactedRow, except when the row position is the position stored in 1. 3. Expanded rows start off with the same height as compacted rows and then change height smoothly with a value animator, after the expansion is done a boolean in the recycler adapter is set to true. I am still experimenting with different approaches, but what this does is:Zygote
Pro: Expanded items are consistent in the recyclerview (no multiple expanded rows because of view recycling), Height gets transformed smoothly Con: Complex row layout changes between compacted and expanded layout will be some not neglegible amount of manual code work, I would love to somehow implement LayoutTransitions from the framework to automate this process, but I haven't found out why they do not work and how to fix that yetZygote
See this gist for the expansion of the row in onBindViewHolder gist.github.com/anonymous/71e7c9788ee3545d21bc The trick I am using is, that the expanded row layout looks exactly like the compacted view layout, before the animator starts, the animator then changes one layout into the other. It all works perfectly, but it is still somewhat of a workload to implement the layout transition (you have to interpolate every view item that you want to move), I would still like to use the TransitionFramework with shared elements. I can give you more code if necessaryZygote
Also see this question #27259192Zygote
@A.Steenbergen, check my answer, that involves itemAnimator.Biauriculate
S
11

OK, So I have found a solution that does not involve calling notifyItemChanged so the view is not replaced. The con is that you have to manualy check the view consistency. For that, I have created a small library that allows exactly what I was looking for, for 5 loc in the ViewHolder.

The trick is to animate height change manualy rather than using a LayoutTransition.

The demo project can be found here.

Soucy answered 15/12, 2014 at 15:36 Comment(6)
Isn't that basically what I said and now you are crediting yourself for this answer?Zygote
I don't believe so, but I'm sorry if you feel I've rob you in any sort. This solution does not use notifyDataSetChanged and "manually" manages cell states, preventing "cell exchange". Furthermore, I have put this as an answer and not a comment because I am providing a demo project that illustrates how I do this. Please, do post your answer with your demo project here and I will be happy to credit you as the answer :)Soucy
"lp.height = (int) animation.getAnimatedValue(); _view.setLayoutParams(lp);" - that lines, taken from demo project, is really-really slow way to animate anything. I wrote about that here and proposed solutions (not very proud of) #12730330Reba
this is a bad example to access a View directly on a RecycleView... this is not a solution.Hendeca
The link is dead, and this was basically a link-only answerDonica
@Donica The link (MEGA) still works (June 2020), at least for me.Malena
S
0

You should just use notifyItemChanged(getPosition(), new Object()) instead.

In your adapter, override onBindViewHolder(GigExtraViewHolder holder, int position, List<Object> payloads) , if payloads is null or empty do your original bind logics, else, just do your own expand/collapse animation.

Shortcircuit answered 27/9, 2016 at 8:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.