Android - scaling animation using AnimatorSet

Official View Zoom uses the AnimatorSet to zoom in on the View . This creates the illusion of a downward movement as the gaze widens. Later, the AnimatorSet simply replayed to create the illusion of a decrease.

zoom-in with downward movement What I need to implement is the exact opposite. I need to start with an expanded view and reduce it to a smaller view with an upward movement:

zoom-out with upward movement It seems I cannot use the spread code in the example. This example assumes that you first zoom in and out, and then compress it back to the thumbnail.

Here is what I have tried so far. My xml layout

 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:background="#1999da"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:orientation="horizontal" android:layout_gravity="center" android:gravity="center"> <!-- The final shrunk image --> <ImageView android:id="@+id/thumb_button_1" android:layout_width="wrap_content" android:layout_height="50dp" android:layout_marginRight="1dp" android:visibility="invisible"/> </LinearLayout> </LinearLayout> <!-- The initial expanded image that needs to be shrunk --> <ImageView android:id="@+id/expanded_image" android:layout_width="wrap_content" android:layout_height="125dp" android:layout_gravity="center" android:src="@drawable/title_logo_expanded" android:scaleType="centerCrop"/> </FrameLayout> 

And here is the method that performs the reduction operation. I basically tried changing the procedure in the tutorial:

 private void zoomImageFromThumbReverse(final View expandedImageView, int imageResId, final int duration) { // If there an animation in progress, cancel it immediately and proceed with this one. if (mCurrentAnimator != null) { mCurrentAnimator.cancel(); } // Load the low-resolution "zoomed-out" image. final ImageView thumbView = (ImageView) findViewById(R.id.thumb_button_1); thumbView.setImageResource(imageResId); // Calculate the starting and ending bounds for the zoomed-in image. This step // involves lots of math. Yay, math. final Rect startBounds = new Rect(); final Rect finalBounds = new Rect(); final Point globalOffset = new Point(); // The start bounds are the global visible rectangle of the container view (ie the FrameLayout), and the // final bounds are the global visible rectangle of the thumbnail. Also // set the container view offset as the origin for the bounds, since that's // the origin for the positioning animation properties (X, Y). findViewById(R.id.container).getGlobalVisibleRect(startBounds, globalOffset); thumbView.getGlobalVisibleRect(finalBounds); startBounds.offset(-globalOffset.x, -globalOffset.y); finalBounds.offset(-globalOffset.x, -globalOffset.y); // Adjust the start bounds to be the same aspect ratio as the final bounds using the // "center crop" technique. This prevents undesirable stretching during the animation. // Also calculate the start scaling factor (the end scaling factor is always 1.0). float startScale; if ((float) finalBounds.width() / finalBounds.height() > (float) startBounds.width() / startBounds.height()) { // Extend start bounds horizontally startScale = (float) startBounds.height() / finalBounds.height(); float startWidth = startScale * finalBounds.width(); float deltaWidth = (startWidth - startBounds.width()) / 2; startBounds.left -= deltaWidth; startBounds.right += deltaWidth; } else { // Extend start bounds vertically 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 expanded-image and show the zoomed-out, thumbnail view. When the animation begins, // it will position the zoomed-in view in the place of the thumbnail. expandedImageView.setAlpha(0f); thumbView.setVisibility(View.VISIBLE); // Set the pivot point for SCALE_X and SCALE_Y transformations to the top-left corner of // the zoomed-in view (the default is the center of the view). thumbView.setPivotX(0f); thumbView.setPivotY(0f); // Construct and run the parallel animation of the four translation and scale properties // (X, Y, SCALE_X, and SCALE_Y). AnimatorSet set = new AnimatorSet(); set .play(ObjectAnimator.ofFloat(thumbView, View.X, startBounds.left, finalBounds.left)) .with(ObjectAnimator.ofFloat(thumbView, View.Y, startBounds.top, finalBounds.top)) .with(ObjectAnimator.ofFloat(thumbView, View.SCALE_X, startScale, 1f)) .with(ObjectAnimator.ofFloat(thumbView, View.SCALE_Y, startScale, 1f)); //set.setDuration(mShortAnimationDuration); set.setDuration(duration); set.setInterpolator(new DecelerateInterpolator()); set.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { mCurrentAnimator = null; } @Override public void onAnimationCancel(Animator animation) { mCurrentAnimator = null; } }); set.start(); mCurrentAnimator = set; // Upon clicking the zoomed-out image, it should zoom back down to the original bounds // and show the thumbnail instead of the expanded image. final float startScaleFinal = startScale; thumbView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (mCurrentAnimator != null) { mCurrentAnimator.cancel(); } // Animate the four positioning/sizing properties in parallel, back to their // original values. AnimatorSet set = new AnimatorSet(); set .play(ObjectAnimator.ofFloat(thumbView, View.X, startBounds.left)) .with(ObjectAnimator.ofFloat(thumbView, View.Y, startBounds.top)) .with(ObjectAnimator .ofFloat(thumbView, View.SCALE_X, startScaleFinal)) .with(ObjectAnimator .ofFloat(thumbView, View.SCALE_Y, startScaleFinal)); //set.setDuration(mShortAnimationDuration); set.setDuration(duration); set.setInterpolator(new DecelerateInterpolator()); set.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { expandedImageView.setAlpha(1f); thumbView.setVisibility(View.GONE); mCurrentAnimator = null; } @Override public void onAnimationCancel(Animator animation) { expandedImageView.setAlpha(1f); thumbView.setVisibility(View.GONE); mCurrentAnimator = null; } }); set.start(); mCurrentAnimator = set; } }); } 

I call this method in onCreate() as follows:

 final View expandedImageView = findViewById(R.id.expanded_image); new Handler().postDelayed(new Runnable(){ public void run() { zoomImageFromThumbReverse(expandedImageView, R.drawable.title_logo_min, 1000); }}, 1000); 

Good thing these guys. He does not work. I do not understand why. The demo works fine, so why doesn't it work? Take the gander and tell me if I'm crazy.

Can anyone recognize the mistake? Or point me in the right direction? All help would be greatly appreciated.

+15
java android android-animation objectanimator viewpropertyanimator
Apr 28 '15 at 5:31 on
source share
2 answers

This is the solution I used in the end:

 private void applyAnimation(final View startView, final View finishView, long duration) { float scalingFactor = ((float)finishView.getHeight())/((float)startView.getHeight()); ScaleAnimation scaleAnimation = new ScaleAnimation(1f, scalingFactor, 1f, scalingFactor, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); scaleAnimation.setDuration(duration); scaleAnimation.setInterpolator(new AccelerateDecelerateInterpolator()); Display display = getWindowManager().getDefaultDisplay(); int H; if(Build.VERSION.SDK_INT >= 13){ Point size = new Point(); display.getSize(size); H = size.y; } else{ H = display.getHeight(); } float h = ((float)finishView.getHeight()); float verticalDisplacement = (-(H/2)+(3*h/4)); TranslateAnimation translateAnimation = new TranslateAnimation(Animation.ABSOLUTE, 0, Animation.ABSOLUTE, 0, Animation.ABSOLUTE, 0, Animation.ABSOLUTE, verticalDisplacement); translateAnimation.setDuration(duration); translateAnimation.setInterpolator(new AccelerateDecelerateInterpolator()); AnimationSet animationSet = new AnimationSet(false); animationSet.addAnimation(scaleAnimation); animationSet.addAnimation(translateAnimation); animationSet.setFillAfter(false); startView.startAnimation(animationSet); } 

The key factor here is the value of toYDelta in the TranslateAnimation parameter:

 toYDelta = (-(H/2)+(3*h/4)); 

Understanding why this works is the main thing. The rest is basically simple.

+5
May 11 '15 at 3:53
source share

Well, I think you want to zoom out from the upward movement of your images and descriptions. I can not understand your code, it seems to me too complicated (I am noob). Now I have done what you need using the following code. First I declare a relative layout with an image representation, this relative layout will be a container. I set the initial width of the width, but we will change it later from the code.

 <RelativeLayout android:id="@+id/container" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <ImageView android:id="@+id/imageView" android:layout_width="400dp" android:layout_height="200dp" android:layout_centerHorizontal="true" android:scaleType="fitXY" android:src="@drawable/my_image"/> </RelativeLayout> 

Now in action, I installed a listener to change the layout to get the actual size of the container. Then set the layout for ImageView.

 public class MainActivity extends Activity { ImageView im; RelativeLayout container; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Logger.init().hideThreadInfo().setMethodCount(0); setContentView(R.layout.activity_main); im = (ImageView) findViewById(R.id.imageView); container = (RelativeLayout) findViewById(R.id.container); container.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { setInitialPos(); container.getViewTreeObserver().removeOnGlobalLayoutListener(this); } }); } int width; int height; int topMargin; private void setInitialPos() { Logger.e("container: " + container.getWidth() + " x " + container.getHeight()); width = container.getWidth(); height = 400; RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) im.getLayoutParams(); layoutParams.width = width; layoutParams.height = height; topMargin = (container.getHeight() - height) / 2; layoutParams.topMargin = topMargin; im.setLayoutParams(layoutParams); startAnimation(); } 

We have to animate three things here: width, height and topMargin (for positioning). So, I declare three variables for the initial position of the animator, and I calculate them at the initial placement. Now we need to animate these three variables at the same time, which is easy.

  private void startAnimation() { AnimatorSet animator = new AnimatorSet(); Animator widthAnimator = ObjectAnimator.ofInt(this, "width", width, 200); widthAnimator.setInterpolator(new LinearInterpolator()); Animator heightAnimator = ObjectAnimator.ofInt(this, "height", height, 100); heightAnimator.setInterpolator(new LinearInterpolator()); Animator marginAnimator = ObjectAnimator.ofInt(this, "topMargin", topMargin, 0); marginAnimator.setInterpolator(new LinearInterpolator()); animator.playTogether(widthAnimator, heightAnimator, marginAnimator); animator.setDuration(3000); animator.start(); } public void setWidth(int w) { RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) im.getLayoutParams(); layoutParams.width = w; im.setLayoutParams(layoutParams); } public void setHeight(int h) { RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) im.getLayoutParams(); layoutParams.height = h; im.setLayoutParams(layoutParams); } public void setTopMargin(int m) { RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) im.getLayoutParams(); layoutParams.topMargin = m; im.setLayoutParams(layoutParams); } } 
+2
May 7 '15 at 14:08
source share



All Articles