Conductor: Transition between two Controllers with RecyclerViews not working

Created on 9 Jan 2017  Â·  17Comments  Â·  Source: bluelinelabs/Conductor

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?

Most helpful comment

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.

All 17 comments

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.

Thx @sockeqwe

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

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.

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 :)

I know what you are talking about, I just spend an hour to quickly
prototype this. Unfortunately I don't have too much time this week to dive
into a solution for that (maybe just returning false in OnPredrawListener
to not draw the view is enough). As already said in my previous message,
probably using LayoutChangeListener on the recyclerview in ChangeHandler,
and then set an OnPredrawListener from ChangeHandler for the viewholder is
a more elegant approach (instead of the callback thing I have implemented
right now) and avoids this issue from the very beginning.

theodm notifications@github.com schrieb am Sa., 14. Jan. 2017, 11:20:

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 :)

—
You are receiving this because you were mentioned.

Reply to this email directly, view it on GitHub
https://github.com/bluelinelabs/Conductor/issues/193#issuecomment-272615153,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAjnrkrK4H_ysmLU4ilr2_d1Vh2UVP4gks5rSKFpgaJpZM4LesCb
.

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

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

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

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

I will do that :)
Hannes Dorfmann notifications@github.com schrieb am So. 15. Jan. 2017 um
09:42:

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

—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/bluelinelabs/Conductor/issues/193#issuecomment-272681776,
or mute the thread
https://github.com/notifications/unsubscribe-auth/APvPP5E3iQQcpG0P9kb2i38VWUkZl7IQks5rSdvqgaJpZM4LesCb
.

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.

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?

I think such a generic Change Handler can be implemented entirely without
any callbacks from controller since the ChangeHandler itself can register
itself as OnPreDrawListener. Im thinking of a ChangeHandlern with a
constructor with a String (or a list of strings if you have multiple
elements) with the transition names of your shared element transitions.

I think this is a more elegant solution then working with callbacks.

theodm notifications@github.com schrieb am Mo., 16. Jan. 2017, 07:56:

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?

—
You are receiving this because you were mentioned.

Reply to this email directly, view it on GitHub
https://github.com/bluelinelabs/Conductor/issues/193#issuecomment-272786736,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAjnrjSEY8RsVbg1wia8qx3CoyuRGH98ks5rSxScgaJpZM4LesCb
.

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.

Any updates?

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.

@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

Was this page helpful?
0 / 5 - 0 ratings

Related issues

dmarcato picture dmarcato  Â·  4Comments

adipascu picture adipascu  Â·  6Comments

ZakTaccardi picture ZakTaccardi  Â·  8Comments

tomblenz picture tomblenz  Â·  4Comments

lenguyenthanh picture lenguyenthanh  Â·  7Comments