14

I'm trying to implement a shared elements transition when 2 ImageViews from one screen go to the next screen. one of the images has a scaleType of centerCrop on both screen. The problem I'm facing is that when the transition starts the image is going to it's original size (before the crop) just before the animating it to the next screen. when it's reaches the next screen it reaches it with it's original size and only then it's cropped to the target image view.

The whole experience is not smooth and really jumpy to the eye. The code I use is:

public void animateIntent(View view, ArticleCoverData articleCoverData) {
    Intent intent = new Intent(mActivity, ReaderActivity.class);
    intent.putExtra(Constants.ARTICLE_DATA, articleCoverData);

    // The view animation will start from
    View coverStartView = view.findViewById(R.id.coverImage);
    View coverDiagonalDecoratorStartView = view.findViewById(R.id.diagonal_image_decorator);

    Pair<View, String> pair1 = Pair.create(coverStartView, mActivity.getString(R.string.transition_timeline_cover_image_to_reader_name));
    Pair<View, String> pair2 = Pair.create(coverDiagonalDecoratorStartView, mActivity.getString(R.string.transition_timeline_cover_image_diagonal_decorator_to_reader_name));

    ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(mActivity, pair1, pair2);
    ActivityCompat.startActivity(mActivity, intent, options.toBundle());
}

The image is loaded from the web using Glide with following code:

Glide.with(mActivity).load(articleCoverData.mImageUrl).skipMemoryCache(true).diskCacheStrategy(DiskCacheStrategy.SOURCE).dontTransform().into(holder.coverImage);

On the receiving side the image is loaded as follows:

Glide.with(mContext).load(mData.mImageUrl).skipMemoryCache(true).diskCacheStrategy(DiskCacheStrategy.SOURCE).into(mCoverImageView);

In the style I defined:

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Add this line -->
    <item name="android:windowContentTransitions">true</item>

    <!-- This is the color of the Status bar -->
    <item name="colorPrimaryDark">@color/transparent</item>

    <!-- specify shared element transitions -->
    <item name="android:windowSharedElementEnterTransition">
        @transition/change_image_transform</item>
    <item name="android:windowSharedElementExitTransition">
        @transition/change_image_transform</item>
</style>

When change_image_transform is:

<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    <changeImageTransform/>
</transitionSet>

Edit: Tried to change the transition to:

<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    <changeBounds/>
    <changeTransform/>
    <changeImageTransform/>
</transitionSet>

or

<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    <changeBounds/>
    <changeImageTransform/>
</transitionSet>

But the result is the same.

The question is: Am I missing something here, is there something else I need to do to make this experience smother? or this is an Android bug?

halfer
  • 19,824
  • 17
  • 99
  • 186
Emil Adz
  • 40,709
  • 36
  • 140
  • 187

4 Answers4

4

Finally, managed to solve this issue. desired result was achieved using the following code and configuration:

private static void startAnimatedTransitionIntent(Activity context, View view, ArticleCoverData articleCoverData) {
    Intent intent = new Intent(context, ReaderActivity.class);
    intent.putExtra(Constants.ARTICLE_DATA, articleCoverData);

    View coverStartView = view.findViewById(R.id.coverImage);
    View coverDiagonalDecoratorStartView = view.findViewById(R.id.diagonal_image_decorator);

    Window window = context.getWindow();
    View decor = window.getDecorView();
    View navigationBarStartView = decor.findViewById(android.R.id.navigationBarBackground);

    List<Pair<View, String>> pairs = new ArrayList<>();
    pairs.add(Pair.create(coverStartView, context.getString(R.string.transition_timeline_cover_image_to_reader_name)));
    pairs.add(Pair.create(coverDiagonalDecoratorStartView, context.getString(R.string.transition_timeline_cover_image_diagonal_decorator_to_reader_name)));

    if (navigationBarStartView != null) {
        pairs.add(Pair.create(navigationBarStartView, Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME));
    }

    ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(context, pairs.toArray(new Pair[pairs.size()]));
    ActivityCompat.startActivity(context, intent, options.toBundle());
}

I'm loading the images into the image view using Picasso and the following code:

Picasso.with(mActivity).load(articleCoverData.mImageUrl).into(articleVH.coverImage);

On the receiving side I apply the image in the same way:

Picasso.with(mContext).load(mData.mImageUrl).into(mCoverImageView);

The style:

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="android:statusBarColor">@color/app_red</item>
    <item name="android:windowContentTransitions">true</item>

    <!-- This is the color of the Status bar -->
    <item name="colorPrimaryDark">@color/transparent</item>
    <item name="android:windowDrawsSystemBarBackgrounds">true</item>

    <!--<item name="android:windowEnterTransition">@transition/cheeses_enter</item>-->
    <!--<item name="android:windowExitTransition">@transition/cheeses_exit</item>-->

    <item name="android:windowSharedElementEnterTransition">@transition/cheeses_enter</item>
    <item name="android:windowSharedElementExitTransition">@transition/cheeses_exit</item>
</style>

both transitions: cheeses_enter and cheeses_exit are:

<?xml version="1.0" encoding="utf-8"?>
<transitionSet>
    <changeBounds />
    <changeTransform />
    <changeClipBounds />
    <changeImageTransform />
</transitionSet>

The important thing is to apply your scale type using the ImageView scaleType parameter and not using Picasso crop or Glide crop.

Because if you do that the the crop and the un-crop action would perform as part of the transition.

Emil Adz
  • 40,709
  • 36
  • 140
  • 187
  • 1
    This was the key "The important thing is to apply your scale type using the ImageView scaleType parameter and not using Picasso crop or Glide crop." Thanks – Roberto Jul 25 '17 at 09:50
  • 3
    In my case, starting activity is having CenterCrop property and in opening its smooth but on back animation, it is causing issue. First animation scales down to the height of cell and then fills the width. – Usman Rana Nov 27 '18 at 14:11
  • 1
    what change should i make? as opening is fine, on return the scaling is not matching. – Usman Rana Nov 28 '18 at 10:38
  • @UsmanRana, I have facing same issue with centerCrop. Have you found solution. Please share – mukesh Dec 10 '19 at 09:49
  • @mukesh default transition will allow you to suffer jerk at 1 place, either at opening or at closing (if you change scaleType in onTransitionStart method). The idea that can resolve this to not use centerCrop, but apply zoom on the view by setting scaleType. – Usman Rana Dec 18 '19 at 10:47
4

I am facing the same issue and I already use dontAnimate() + dontTransform() in my Glide load request.

For me, my solution is to remove:

clipToPadding="false"
clipChildren="false"

from my ImageView's container ViewGroup.

Those properties leave my image unclipped when using scaleType="centerCrop" during the Shared element transition.

Tam Huynh
  • 2,026
  • 1
  • 16
  • 20
2

Just to give an answer (and a bit more explanation) for anyone bumping this problem.

First of all just using <changeImageTransform/> and <changeBounds/> is enough to make the transition.

The problem is mainly how Glide handles the imageview/drawable measurement over the time. There is some delay and for this reason no transition animation is created.

Picasso provides quick enough the values of the location of the image (matrix) based on scaletype and the transitions work.

Diolor
  • 13,181
  • 30
  • 111
  • 179
0

In your change_image_transform.xml, change to this

<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    <changeImageTransform/>
    <changeBounds/>
</transitionSet>
Fuyuba
  • 191
  • 1
  • 8
  • I see that you pass the image url to new activity and then in new activity, you use glide to load it again? Try pass the bitmap of image, i think it'll smoother – Fuyuba Jun 22 '16 at 08:23
  • yes, but the image is cached by glide, so the second time it's used, it's used from the disk and not loaded again from web. – Emil Adz Jun 22 '16 at 08:26