Shared Element with scaleType centerCrop transition is jumpy
Asked Answered
L

4

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?

Leak answered 22/6, 2016 at 7:59 Comment(0)
L
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.

Leak answered 28/6, 2016 at 12:58 Comment(5)
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." ThanksCannular
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.Ogpu
what change should i make? as opening is fine, on return the scaling is not matching.Ogpu
@UsmanRana, I have facing same issue with centerCrop. Have you found solution. Please shareAccompanyist
@Accompanyist 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.Ogpu
K
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.

Kaylyn answered 3/5, 2018 at 15:18 Comment(0)
A
3

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.

Adagietto answered 21/1, 2017 at 23:52 Comment(0)
P
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>
Pelvis answered 22/6, 2016 at 8:5 Comment(2)
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 smootherPelvis
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.Leak

© 2022 - 2024 — McMap. All rights reserved.