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.