prom85 prom85 - 4 months ago 34
Android Question

Activity Transition between two RecyclerViews

I animate from one

RecyclerView
with a
GridLayout
to another one. It displays images like a gallery.

The parent
RecyclerView
shows the first 3 elements of a folder, the child
RecyclerView
shows the complete content in a Grid...

main adapter

@Override
public void onBindViewHolder(FolderViewHolder holder, int position) {
holder.transitionPairs.clear();
for (int i = 0; i < 3; i++) {
ImageView iv = holder.getIconView(i);
Image image = mItems.get(position).getImages().get(i);
holder.transitionPairs.add(new Pair(iv, image.getTransitionName()));
}


child adapter

@Override
public void onBindViewHolder(ImageViewHolder holder, int position) {

ViewCompat.setTransitionName(holder.icon, mItems.get(position).getTransitionName());
}



  • both adapters set the SAME transition name, it's generated from the image uri, so it's surely unique



I start the child view like following:

Intent intent = new Intent(getActivity(), ImagesActivity.class);
intent.putExtra("folderIndex", pos);
intent.putExtra("folder", item);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(), vh.getTransitionPairs().toArray(new Pair[vh.getTransitionPairs().size()]));
getActivity().startActivity(intent, options.toBundle());
}
else
getActivity().startActivity(intent);


The transition pairs are saved in the
ViewHolder
and are generated whenever the
ViewHolder
is bound, as you can see in the
ViewHolders
code above

PROBLEM

Showing the child view is not animated, going back is animated...

Answer

sorry, but that's a documented limitation of the transition framework.

https://developer.android.com/intl/es/training/transitions/overview.html#Limitations

Limitations

This section lists some known limitations of the transitions framework: - Classes that extend AdapterView, such as ListView, manage their child views in ways that are incompatible with the transitions framework. If you try to animate a view based on AdapterView, the device display may hang.

I know it states ListView but is the same for RecyclerView. The Animation framework must capture the transitionName value as soon as the views are laid out, and the ListView and RecyclerView take extra time to get an adapter, create the child views, laid out those child views on the screen and then have them bound to the data. The transition framework can't "see" those views.

My best suggestion for you is to have a "fake" item from your adapter inflated on the actual activity laid out, just for the transition to run onto, and then onStart you set this view go GONE.

edit:

based on the link supplied by the OP (Having issues using Android 5.0 Activity transitions onto an activity with a ViewPager) I've ended developing my own, but very similar solution.

On my own project I have a GridLayoutManager with about 7 elements per screen and animates to a LinearLayoutManager with mostly 1 per view (but sometimes you can see corners of the one before or after).

In the linked response the solution is to use Activity.postponeEnterTransition() and Activity.startPostponedEnterTransition() and OnPreDrawListener to proper deliver all the transition.

The important fact to remember is that the view must be attached to the window for the transition framework to capture it. So I used the OnAttachStateChangeListener to dispatch the transition, instead of OnPreDraw. That should make it to a subtly smooth transition.

// somewhere on the `onBind` code I put that
itemView.addOnAttachStateChangeListener(listener);

// and this is the listener
private final View.OnAttachStateChangeListener listener = new View.OnAttachStateChangeListener() {
         @Override public void onViewAttachedToWindow(View v) {
            activity.startPostponedEnterTransition();
            v.removeOnAttachStateChangeListener(this);
         }

         @Override public void onViewDetachedFromWindow(View v) {
            v.removeOnAttachStateChangeListener(this);
         }
      };

it's working fine for me.

Comments