Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transition between two Controllers with RecyclerViews not working #193

Closed
Bodo1981 opened this issue Jan 9, 2017 · 17 comments
Closed

Transition between two Controllers with RecyclerViews not working #193

Bodo1981 opened this issue Jan 9, 2017 · 17 comments

Comments

@Bodo1981
Copy link

Bodo1981 commented Jan 9, 2017

My setup:

Controller A (movie list):

  • RecyclerView with ImageViewHolder

Controller B (movie detail):

  • RecyclerView with HeaderImageViewHolder

Problem:
I want to add a transition from Controller A to Controller B with the standard ArcFadeMoveChangeHandler to have a beautiful animation between these two controllers. I added unique transitionNames to the movie items and forward the clicked item transitionName to Controller B. In the onBindView of the adapter of Controller B this transitionName is set to the HeaderImageViewHolder, but the transition is not working. I think it is because the transition is started before recyclerview is ready

Question:
I searched and found that it is possible to postpone the enter transition, but this is not working. how can i get this transtion from one item in a recyclerview to an item in another controllers recyclerview to work correctly?

@sockeqwe
Copy link
Contributor

sockeqwe commented Jan 9, 2017

yep, you are on the right track. The Problem is that ArcFadeMoveChangeHandler extends TransitionChangeHandler waits until the root view of each controller has been laid out ready to be drawn on the screen and then the Transition kicks in and animates all the things "magically" to the correct position. TransitionChangeHandler does that via TransitionManager.beginDelayedTransition() which basically internally uses a OnPreDrawListener to determine when both views are ready (laid out / ready to be drawn) and then runs the shared element transition.

https://github.com/bluelinelabs/Conductor/blob/develop/conductor/src/main/java/com/bluelinelabs/conductor/changehandler/TransitionChangeHandler.java#L43

The problem with RecyclerView is that while the RecyclerView itself might be ready (laid out / ready to be drawn), his children (ViewHolder) are not. ViewHolder's are typically be laid out by the LayoutManager after the RecyclerView has been laid out in the next round of the main thread looper (since RecyclerView 25.1.0 there is a feature setInitialItemPrefetchCount() which optimizes the time when the ViewHolders will be laid out, but there is no guarantee that this will happen right after RecyclerView has been laid out).

So the solution for your problem is that you have to wait until your ViewHolder is ready (laid out / ready to be drawn), i.e. add a OnPreDrawListener to your target ViewHolder. There is no difference here with traditional Fragments / Activities except that Fragments/ Activities offer a postponeEnterTransition() and startPostponedEnterTransition(). Conductor doesn't have such a API / methods. And you don't need them.

In contrast to Fragments / Activity, you as developer are under full control when animations are started and you can add listener to get notified when they are finished. With Conductor you have to use ChangeListener which is a great concept for separation of concerns (thumbs up @EricKuck for this api decision). In ChangeListner.performChange() you should be able to set up all the required listeners (also Controller. onChangeStarted() could be useful to get the reference to the change handler to register a OnPreDrawListener), once they trigger use the TransitionManager to start the share element transition (don't forget to set transition name) and then finally call changeListener.onChangeCompleted(); to inform Conductor that a ChangeHandler has completed his work.

Hope that helps.

@Bodo1981
Copy link
Author

Bodo1981 commented Jan 9, 2017

Thx @sockeqwe

Sounds good. Can you please give me a short example where to set the listeners and how this should be implemented?

@sockeqwe
Copy link
Contributor

Ugly as hell, but animates a shared element:

https://github.com/sockeqwe/conductor-shared-element-transition/blob/master/app/src/main/java/com/hannesdorfmann/conductor_shared_element_transition/DetailsToListChangeHandler.kt

There might be better ways to implement that. I.e. I guess we could add a LayoutListener to the RecyclerView from in ChangeHandler and then once children (ViewHolders) are added we could iterate over the item and add an OnPredrawListener (also directly from ChangeHandler) to get notified when the SharedElement Transition is ready to run.

Right now in my example code I use controller.onChangeStarted() to register the ChangeHandler as "callback" to get notified once the target ViewHolder is ready to start the shared element transition. This is a little bit awkward.

@theodm
Copy link

theodm commented Jan 14, 2017

Hello,

i just downloaded your example here: https://github.com/sockeqwe/conductor-shared-element-transition/blob/master/app/src/main/java/com/hannesdorfmann/conductor_shared_element_transition/DetailsToListChangeHandler.kt

I noticed, the screen is flashing when going back from the Detail View to the List View. It shortly shows the fully drawn List View, then hides it and then starts the shared element transition.

I think it's due to adding the listView to the layout before removing it again. (I know we are doing that to catch the onPreDrawListener())

I sadly couldn't get the "screenrecord" command working.

Do you have any solutions for this problem?

Thank you for your time :)

@sockeqwe
Copy link
Contributor

sockeqwe commented Jan 14, 2017 via email

@theodm
Copy link

theodm commented Jan 14, 2017

No rush, it's great what you are doing :)

I'm going to to try these approaches, will respond back.

@theodm
Copy link

theodm commented Jan 14, 2017

Returning false from OnPreDrawListener is enough. Thank you for your help :)

@sockeqwe
Copy link
Contributor

Cool! Do you want to send me a pull request so that others can benefit from this working example too?

@theodm
Copy link

theodm commented Jan 15, 2017 via email

@EricKuck
Copy link
Member

What do you guys think of the possibility of adding this directly to the library? This seems like a pretty common use case with a not so obvious solution. I was thinking this could just be added as an option to the existing set of TransitionChangeHanders. There could either be a waitsForCallback option, where the developer has to call back once a set of conditions is met, or even a waitsForPredraw, which could handle all this internally. The waitsForCallback option would allow developes to do whatever they wanted with it, but would make the most common use case a little more tedious, while the waitsForPredraw would ideally do the most common use case automatically, but not allow much flexibility.

@theodm
Copy link

theodm commented Jan 16, 2017

To clarify the terms here:

WaitsForPredraw would be the design, that the ChangeHandler waits for the target view to be predrawn and WaitsForCallback would be the design that the ChangeHandler waits for the target controller to tell when it's ready?

@sockeqwe
Copy link
Contributor

sockeqwe commented Jan 16, 2017 via email

@theodm
Copy link

theodm commented Jan 16, 2017

I think the only benefit by using a Callback is, that the controller can preload stuff, which is needed for the transition (for example: an Image inside the recyclerview). This was my orginal use case, but I discarded that approach, because the preloading delayed the transition.

@sdrygalski
Copy link

Any updates?

@EricKuck
Copy link
Member

I've decided against putting this in the core lib for now, as any API I could make for it would be kind of confusing (if anyone has ideas here, I'm all ears).

I did add a demo of this to the demo app in the repo though. The change handler of interest is this one:

https://github.com/bluelinelabs/Conductor/blob/develop/demo/src/main/java/com/bluelinelabs/conductor/demo/changehandler/SharedElementDelayingChangeHandler.java

Thanks for the help on this @sockeqwe! Your demo pointed me in the right direction to get all this working.

@spung
Copy link

spung commented Mar 1, 2018

@EricKuck was the demo removed from the demo app? That link gives me a 404 and I don't see it in the demo app

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants