Android image viewer zoom animation

The android image viewer zoom animation tutorial describes the steps of how to add animations to an image view when zooming from a thumbnail to full screen image.

Get Code

The code can be found on github from the following instructions below

https://github.com/mobapptuts/android_image_viewer.git Tag

image-viewer-thumb-animation

or you can run this command

git clone https://github.com/mobapptuts/android_image_viewer.git –branch

image-viewer-thumb-animation

Code Samples

In the layout file set the dimensions of the image view & make the pinch zoom image view invisible

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="mobapptut.com.imageviewer.ImageViewMainActivity">

    <ImageView
        android:layout_centerInParent="true"
        android:layout_width="200dp"
        android:layout_height="150dp"
        android:id="@+id/imageView"
        />

    <mobapptut.com.imageviewer.PinchZoomImageView
        android:id="@+id/pinchZoomImageView"
        android:visibility="invisible"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentTop="true"
        android:layout_alignParentStart="true" />
</RelativeLayout>

 

Move the image Uri to an Activity member

private Uri mImageUri;

 Add an animator & int for the animation duration time

private Animator mCurrentAnimator;
 private int mLongAnimationDuration;;

 Initialise the animation duration member in the onCreate method

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_image_view_main);

        mImageView = (ImageView) findViewById(R.id.imageView);
        mPinchZoomImageView = (PinchZoomImageView) findViewById(R.id.pinchZoomImageView);
        mImageView.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                // Toast.makeText(getApplicationContext(), "ImageView long pressed!", Toast.LENGTH_SHORT).show();
                zoomImageFromThumb();
                return true;
            }
        });

        mLongAnimationDuration = getResources().getInteger(android.R.integer.config_longAnimTime);

        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("image/*");
        startActivityForResult(intent, REQUEST_OPEN_RESULT_CODE);
    }

 Create a method for zooming from image thumb

Check to see if there is an animation active and cancel it so we can proceed with this one

if(mCurrentAnimator != null) {
          mCurrentAnimator.cancel();
      }

Load the hi-resolution image with the Glide image loading libray

Glide.with(this)
               .load(mImageUri)
               .into(mPinchZoomImageView);

Calculate the starting & ending bounds for the full size zoomed in image

final Rect startBounds = new Rect();
final Rect finalBounds = new Rect();
final Point globalOffset= new Point();

mImageView.getGlobalVisibleRect(startBounds);
findViewById(R.id.container)
        .getGlobalVisibleRect(finalBounds, globalOffset);
startBounds.offset(-globalOffset.x, -globalOffset.y);
finalBounds.offset(-globalOffset.x, -globalOffset.y);

Ensure the start bounds has the same aspect ratio as the final bounds

float startScale;
        if( (float) finalBounds.width() / finalBounds.height() >
                (float) startBounds.width() / startBounds.height()) {
            startScale = (float) startBounds.height() / finalBounds.height();
            float startWidth = startScale * finalBounds.width();
            float deltaWidth = (startWidth - startBounds.width()) / 2;
            startBounds.left -= deltaWidth;
            startBounds.right += deltaWidth;
        } else {
            startScale = (float) startBounds.width() / finalBounds.width();
            float startHeight = startScale * finalBounds.height();
            float deltaHeight = (startHeight - startBounds.height()) / 2;
            startBounds.top -= deltaHeight;
            startBounds.bottom += deltaHeight;
        }

Hide the thumbnail & show the zoomed in image view

mImageView.setAlpha(0f);
       mPinchZoomImageView.setVisibility(View.VISIBLE);

Adjust the pivot point for the zoomed in image view

mPinchZoomImageView.setPivotX(0f);
      mPinchZoomImageView.setPivotY(0f);

Set up the translation & scale properties to be run in parallel

AnimatorSet set = new AnimatorSet();
        set
                .play(ObjectAnimator.ofFloat(mPinchZoomImageView, View.X, startBounds.left, finalBounds.left))
                .with(ObjectAnimator.ofFloat(mPinchZoomImageView, View.Y, startBounds.top, finalBounds.top))
                .with(ObjectAnimator.ofFloat(mPinchZoomImageView, View.SCALE_X, startScale, 1f))
                .with(ObjectAnimator.ofFloat(mPinchZoomImageView, View.SCALE_Y, startScale, 1f));
        set.setDuration(mShortAnimationTime);
        set.setInterpolator(new DecelerateInterpolator());
        set.addListener(new AnimatorListenerAdapter() {

            @Override
            public void onAnimationCancel(Animator animation) {
                super.onAnimationCancel(animation);
                mCurrentAnimator = null;
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);

                mCurrentAnimator = null;
            }
        });
        set.start();
        mCurrentAnimator = set;

Full implementation of the zoomImageFromThumb method

private void zoomImageFromThumb() {
        if(mCurrentAnimator != null) {
            mCurrentAnimator.cancel();
        }

        Glide.with(this)
                .load(mImageUri)
                .into(mPinchZoomImageView);

        final Rect startBounds = new Rect();
        final Rect finalBounds = new Rect();
        final Point globalOffset= new Point();

        mImageView.getGlobalVisibleRect(startBounds);
        findViewById(R.id.container)
                .getGlobalVisibleRect(finalBounds, globalOffset);
        startBounds.offset(-globalOffset.x, -globalOffset.y);
        finalBounds.offset(-globalOffset.x, -globalOffset.y);
        float startScale;
        if( (float) finalBounds.width() / finalBounds.height() >
                (float) startBounds.width() / startBounds.height()) {
            startScale = (float) startBounds.height() / finalBounds.height();
            float startWidth = startScale * finalBounds.width();
            float deltaWidth = (startWidth - startBounds.width()) / 2;
            startBounds.left -= deltaWidth;
            startBounds.right += deltaWidth;
        } else {
            startScale = (float) startBounds.width() / finalBounds.width();
            float startHeight = startScale * finalBounds.height();
            float deltaHeight = (startHeight - startBounds.height()) / 2;
            startBounds.top -= deltaHeight;
            startBounds.bottom += deltaHeight;
        }
        mImageView.setAlpha(0f);
        mPinchZoomImageView.setVisibility(View.VISIBLE);

        mPinchZoomImageView.setPivotX(0f);
        mPinchZoomImageView.setPivotY(0f);

        AnimatorSet set = new AnimatorSet();
        set
                .play(ObjectAnimator.ofFloat(mPinchZoomImageView, View.X, startBounds.left, finalBounds.left))
                .with(ObjectAnimator.ofFloat(mPinchZoomImageView, View.Y, startBounds.top, finalBounds.top))
                .with(ObjectAnimator.ofFloat(mPinchZoomImageView, View.SCALE_X, startScale, 1f))
                .with(ObjectAnimator.ofFloat(mPinchZoomImageView, View.SCALE_Y, startScale, 1f));
        set.setDuration(mShortAnimationTime);
        set.setInterpolator(new DecelerateInterpolator());
        set.addListener(new AnimatorListenerAdapter() {

            @Override
            public void onAnimationCancel(Animator animation) {
                super.onAnimationCancel(animation);
                mCurrentAnimator = null;
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);

                mCurrentAnimator = null;
            }
        });
        set.start();
        mCurrentAnimator = set;
    }

 

Android image viewer zoom animation Summary

In the android image viewer zoom animation tutorial we learned how to add property animations to create a visually pleasing zoomed in effect from a thumbnail image.

There are a number of factors to be considered and not in the least the math involved. But adding animations will greatly enhanced the users visual experience of your app.

About The Author
-

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>