Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shared Element with scaleType centerCrop transition is jumpy

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?

like image 913
Emil Adz Avatar asked Jun 22 '16 07:06

Emil Adz


3 Answers

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.

like image 159
Emil Adz Avatar answered Oct 23 '22 16:10

Emil Adz


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.

like image 26
Tam Huynh Avatar answered Oct 23 '22 15:10

Tam Huynh


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.

like image 2
Diolor Avatar answered Oct 23 '22 16:10

Diolor