Search code examples
androidanimationandroid-animationandroid-transitionsshared-element-transition

Determine final size of ImageView shared element after animation


I have a number of shared elements that I'm transitioning between two activities via ActivityOptions.makeSceneTransitionAnimation. When I enter the second activity, I would like to determine what the final target size that the ImageView will be so I can set the appropriate width of the ImageView. I believe that doing so will allow the object to the right of it (also a shared element) to transition smoothly.

The ImageView is a fitStart scaleType with a fixed height and a wrap_content width. The image will be dynamically loaded and is being shrunk down from the first activity to the second, so I don't know the final width. Before the transition starts, I want to set the ImageView's LayoutParams width to match the final width that it will be after the transition ends.

Currently, the wrap_content property of this ImageView is causing the element to the right of it to not know where its final resting place will be, so it goes off the screen to the right before coming back in and landing in the right place.

Here's a gif of the animation. FIRST ELEMENT is the ImageView, animating correctly, and Second Element is the button to the right of the ImageView:

enter image description here

Notice that when the second activity goes back to the first activity, the Second Element transitions smoothly back to the left of the screen because the end position is known.

When I give the ImageView a specific width like 100dp instead of wrap_content (and then change it back to wrap_content after the transition finishes), Second Element moves more smoothly to its final resting place, but since the ImageView will be dynamically loaded and scaled, I won't know the true width of the image in XML beforehand so there would always be a jerky movement of the Second Element when I change it to wrap_content after the transition.

enter image description here

So I would like to determine the target width of the ImageView and set it programmatically before the transitions begin in the second activity so that Second Element knows how far to the left it needs to go. I've tried playing with ViewTreeObserver and TransitionValues but am missing how to get the final width the ImageView is transitioning to.

Is there something I can call in onCreate of the second activity to determine what width the ImageView will end up at after the transition?


EDIT

I tried the solution in this answer but it's reporting back the ImageView's beginning width, not the final width it's transitioning to. For reference, these are the layouts of the two activities I'm switching between: enter image description here

Note that I'm moving AND shrinking the ImageView.

I tweaked the answer's code since I'm not using the support library and had to move to imageView.getViewTreeObserver() in the onPreDraw() call to avoid an IllegalStateException:

postponeEnterTransition();

final ImageView imageView = (ImageView) findViewById(R.id.image_view);
final ViewGroup.LayoutParams imageViewLayoutParams = imageView.getLayoutParams();
final ViewTreeObserver viewTreeObserver = imageView.getViewTreeObserver();

viewTreeObserver.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
  @Override public boolean onPreDraw() {
    imageView.getViewTreeObserver().removeOnPreDrawListener(this);

    // You can acquire the width here
    logoLayoutParams.width = imageView.getWidth();
    Log.i(TAG, "width is " + imageViewLayoutParams.width);
    imageView.setLayoutParams(imageViewLayoutParams);
    // `imageView` has been laid out, but not yet been drawn
    startPostponedEnterTransition();
    return true;
  }
});

The log is reporting a width of 998, which is the larger size of the imageView in Activity 1 (its starting size before the transition). So the Second Element is still flying off the screen.

Now that I have this code in place, I can use the aspect ratio information of the larger image to figure out the final width that the smaller image will be since I know the final height, but is that guaranteed with the fitStart scaleType?

It would be really nice to know if Android can tell me the final size the shared element targeting.


Solution

  • Here's the case: if you change in the second activity the resource from @drawable/first_element to @drawable/first_element_thumb, you'll no long experience that behavior. The downside is, that you'll see a glitch each time the transition is starting, that's because the drawable is being changed from high resolution to a low resolution right before transition starts.

    Here's with 300ms animation time:

    enter image description here

    Here's with 5000ms animation time:

    enter image description here

    If you want the transitioning to be without that glitch, than the solution is to start the transition from high resolution drawable and substitute that drawable to a low resolution whilst transitioning the view. You can see Nick Butcher describing the feature in his "Animatable" talk. You'll end up writing your custom transition class like this one.