101be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik/*
201be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik * Copyright 2012 The Android Open Source Project
301be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik *
401be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik * Licensed under the Apache License, Version 2.0 (the "License");
501be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik * you may not use this file except in compliance with the License.
601be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik * You may obtain a copy of the License at
701be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik *
801be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik *     http://www.apache.org/licenses/LICENSE-2.0
901be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik *
1001be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik * Unless required by applicable law or agreed to in writing, software
1101be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik * distributed under the License is distributed on an "AS IS" BASIS,
1201be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1301be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik * See the License for the specific language governing permissions and
1401be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik * limitations under the License.
1501be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik */
1601be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik
1701be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurikpackage com.example.android.animationsdemo;
1801be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik
1901be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurikimport android.animation.Animator;
2001be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurikimport android.animation.AnimatorListenerAdapter;
2101be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurikimport android.animation.AnimatorSet;
2201be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurikimport android.animation.ObjectAnimator;
2301be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurikimport android.content.Intent;
2401be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurikimport android.graphics.Point;
2501be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurikimport android.graphics.Rect;
2601be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurikimport android.os.Bundle;
2701be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurikimport android.support.v4.app.FragmentActivity;
2801be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurikimport android.support.v4.app.NavUtils;
2901be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurikimport android.view.MenuItem;
3001be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurikimport android.view.View;
3101be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurikimport android.view.animation.DecelerateInterpolator;
3201be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurikimport android.widget.ImageView;
3301be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik
3401be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik/**
3501be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik * A sample showing how to zoom an image thumbnail to full-screen, by animating the bounds of the
3601be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik * zoomed image from the thumbnail bounds to the screen bounds.
3701be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik *
3801be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik * <p>In this sample, the user can touch one of two images. Touching an image zooms it in, covering
3901be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik * the entire activity content area. Touching the zoomed-in image hides it.</p>
4001be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik */
4101be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurikpublic class ZoomActivity extends FragmentActivity {
4201be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik    /**
4301be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik     * Hold a reference to the current animator, so that it can be canceled mid-way.
4401be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik     */
4501be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik    private Animator mCurrentAnimator;
4601be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik
4701be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik    /**
4801be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik     * The system "short" animation time duration, in milliseconds. This duration is ideal for
4901be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik     * subtle animations or animations that occur very frequently.
5001be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik     */
5101be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik    private int mShortAnimationDuration;
5201be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik
5301be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik    @Override
5401be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik    protected void onCreate(Bundle savedInstanceState) {
5501be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        super.onCreate(savedInstanceState);
5601be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        setContentView(R.layout.activity_zoom);
5701be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik
5801be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        // Hook up clicks on the thumbnail views.
5901be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik
6001be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        final View thumb1View = findViewById(R.id.thumb_button_1);
6101be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        thumb1View.setOnClickListener(new View.OnClickListener() {
6201be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik            @Override
6301be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik            public void onClick(View view) {
6401be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                zoomImageFromThumb(thumb1View, R.drawable.image1);
6501be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik            }
6601be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        });
6701be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik
6801be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        final View thumb2View = findViewById(R.id.thumb_button_2);
6901be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        thumb2View.setOnClickListener(new View.OnClickListener() {
7001be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik            @Override
7101be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik            public void onClick(View view) {
7201be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                zoomImageFromThumb(thumb2View, R.drawable.image2);
7301be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik            }
7401be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        });
7501be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik
7601be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        // Retrieve and cache the system's default "short" animation time.
7701be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        mShortAnimationDuration = getResources().getInteger(android.R.integer.config_shortAnimTime);
7801be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik    }
7901be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik
8001be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik    @Override
8101be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik    public boolean onOptionsItemSelected(MenuItem item) {
8201be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        switch (item.getItemId()) {
8301be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik            case android.R.id.home:
8401be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                // Navigate "up" the demo structure to the launchpad activity.
8501be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                // See http://developer.android.com/design/patterns/navigation.html for more.
8601be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                NavUtils.navigateUpTo(this, new Intent(this, MainActivity.class));
8701be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                return true;
8801be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        }
8901be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik
9001be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        return super.onOptionsItemSelected(item);
9101be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik    }
9201be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik
9301be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik    /**
9401be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik     * "Zooms" in a thumbnail view by assigning the high resolution image to a hidden "zoomed-in"
9501be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik     * image view and animating its bounds to fit the entire activity content area. More
9601be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik     * specifically:
9701be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik     *
9801be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik     * <ol>
9901be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik     *   <li>Assign the high-res image to the hidden "zoomed-in" (expanded) image view.</li>
10001be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik     *   <li>Calculate the starting and ending bounds for the expanded view.</li>
10101be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik     *   <li>Animate each of four positioning/sizing properties (X, Y, SCALE_X, SCALE_Y)
10201be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik     *       simultaneously, from the starting bounds to the ending bounds.</li>
10301be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik     *   <li>Zoom back out by running the reverse animation on click.</li>
10401be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik     * </ol>
10501be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik     *
10601be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik     * @param thumbView  The thumbnail view to zoom in.
10701be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik     * @param imageResId The high-resolution version of the image represented by the thumbnail.
10801be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik     */
10901be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik    private void zoomImageFromThumb(final View thumbView, int imageResId) {
11001be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        // If there's an animation in progress, cancel it immediately and proceed with this one.
11101be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        if (mCurrentAnimator != null) {
11201be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik            mCurrentAnimator.cancel();
11301be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        }
11401be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik
11501be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        // Load the high-resolution "zoomed-in" image.
11601be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        final ImageView expandedImageView = (ImageView) findViewById(R.id.expanded_image);
11701be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        expandedImageView.setImageResource(imageResId);
11801be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik
11901be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        // Calculate the starting and ending bounds for the zoomed-in image. This step
12001be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        // involves lots of math. Yay, math.
12101be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        final Rect startBounds = new Rect();
12201be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        final Rect finalBounds = new Rect();
12301be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        final Point globalOffset = new Point();
12401be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik
12501be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        // The start bounds are the global visible rectangle of the thumbnail, and the
12601be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        // final bounds are the global visible rectangle of the container view. Also
12701be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        // set the container view's offset as the origin for the bounds, since that's
12801be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        // the origin for the positioning animation properties (X, Y).
12901be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        thumbView.getGlobalVisibleRect(startBounds);
13001be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        findViewById(R.id.container).getGlobalVisibleRect(finalBounds, globalOffset);
13101be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        startBounds.offset(-globalOffset.x, -globalOffset.y);
13201be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        finalBounds.offset(-globalOffset.x, -globalOffset.y);
13301be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik
13401be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        // Adjust the start bounds to be the same aspect ratio as the final bounds using the
13501be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        // "center crop" technique. This prevents undesirable stretching during the animation.
13601be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        // Also calculate the start scaling factor (the end scaling factor is always 1.0).
13701be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        float startScale;
13801be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        if ((float) finalBounds.width() / finalBounds.height()
13901be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                > (float) startBounds.width() / startBounds.height()) {
14001be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik            // Extend start bounds horizontally
14101be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik            startScale = (float) startBounds.height() / finalBounds.height();
14201be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik            float startWidth = startScale * finalBounds.width();
14301be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik            float deltaWidth = (startWidth - startBounds.width()) / 2;
14401be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik            startBounds.left -= deltaWidth;
14501be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik            startBounds.right += deltaWidth;
14601be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        } else {
14701be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik            // Extend start bounds vertically
14801be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik            startScale = (float) startBounds.width() / finalBounds.width();
14901be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik            float startHeight = startScale * finalBounds.height();
15001be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik            float deltaHeight = (startHeight - startBounds.height()) / 2;
15101be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik            startBounds.top -= deltaHeight;
15201be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik            startBounds.bottom += deltaHeight;
15301be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        }
15401be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik
15501be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        // Hide the thumbnail and show the zoomed-in view. When the animation begins,
15601be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        // it will position the zoomed-in view in the place of the thumbnail.
15701be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        thumbView.setAlpha(0f);
15801be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        expandedImageView.setVisibility(View.VISIBLE);
15901be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik
16001be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        // Set the pivot point for SCALE_X and SCALE_Y transformations to the top-left corner of
16101be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        // the zoomed-in view (the default is the center of the view).
16201be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        expandedImageView.setPivotX(0f);
16301be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        expandedImageView.setPivotY(0f);
16401be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik
16501be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        // Construct and run the parallel animation of the four translation and scale properties
16601be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        // (X, Y, SCALE_X, and SCALE_Y).
16701be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        AnimatorSet set = new AnimatorSet();
16801be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        set
16901be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                .play(ObjectAnimator.ofFloat(expandedImageView, View.X, startBounds.left,
17001be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                        finalBounds.left))
17101be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                .with(ObjectAnimator.ofFloat(expandedImageView, View.Y, startBounds.top,
17201be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                        finalBounds.top))
17301be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                .with(ObjectAnimator.ofFloat(expandedImageView, View.SCALE_X, startScale, 1f))
17401be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                .with(ObjectAnimator.ofFloat(expandedImageView, View.SCALE_Y, startScale, 1f));
17501be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        set.setDuration(mShortAnimationDuration);
17601be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        set.setInterpolator(new DecelerateInterpolator());
17701be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        set.addListener(new AnimatorListenerAdapter() {
17801be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik            @Override
17901be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik            public void onAnimationEnd(Animator animation) {
18001be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                mCurrentAnimator = null;
18101be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik            }
18201be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik
18301be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik            @Override
18401be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik            public void onAnimationCancel(Animator animation) {
18501be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                mCurrentAnimator = null;
18601be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik            }
18701be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        });
18801be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        set.start();
18901be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        mCurrentAnimator = set;
19001be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik
19101be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        // Upon clicking the zoomed-in image, it should zoom back down to the original bounds
19201be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        // and show the thumbnail instead of the expanded image.
19301be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        final float startScaleFinal = startScale;
19401be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        expandedImageView.setOnClickListener(new View.OnClickListener() {
19501be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik            @Override
19601be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik            public void onClick(View view) {
19701be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                if (mCurrentAnimator != null) {
19801be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                    mCurrentAnimator.cancel();
19901be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                }
20001be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik
20101be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                // Animate the four positioning/sizing properties in parallel, back to their
20201be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                // original values.
20301be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                AnimatorSet set = new AnimatorSet();
20401be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                set
20501be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                        .play(ObjectAnimator.ofFloat(expandedImageView, View.X, startBounds.left))
20601be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                        .with(ObjectAnimator.ofFloat(expandedImageView, View.Y, startBounds.top))
20701be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                        .with(ObjectAnimator
20801be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                                .ofFloat(expandedImageView, View.SCALE_X, startScaleFinal))
20901be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                        .with(ObjectAnimator
21001be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                                .ofFloat(expandedImageView, View.SCALE_Y, startScaleFinal));
21101be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                set.setDuration(mShortAnimationDuration);
21201be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                set.setInterpolator(new DecelerateInterpolator());
21301be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                set.addListener(new AnimatorListenerAdapter() {
21401be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                    @Override
21501be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                    public void onAnimationEnd(Animator animation) {
21601be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                        thumbView.setAlpha(1f);
21701be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                        expandedImageView.setVisibility(View.GONE);
21801be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                        mCurrentAnimator = null;
21901be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                    }
22001be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik
22101be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                    @Override
22201be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                    public void onAnimationCancel(Animator animation) {
22301be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                        thumbView.setAlpha(1f);
22401be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                        expandedImageView.setVisibility(View.GONE);
22501be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                        mCurrentAnimator = null;
22601be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                    }
22701be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                });
22801be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                set.start();
22901be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik                mCurrentAnimator = set;
23001be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik            }
23101be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik        });
23201be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik    }
23301be9fd4c0ab5ed70b413f856d3f3a3367863a81Roman Nurik}
234