缩放View

编写:XizhiXu - 原文:<http://developer.android.com/training/animation/zoom.html>

这节课展示怎样实现点击缩放动画,这对相册很有用,他能为相片从缩略图转换成原图并填充屏幕提供动画。

下面展示了触摸缩放动画的效果,它将缩略图扩大并填充屏幕。

0.7 倍 1.0 倍 1.2 倍 1.5 倍 2.0 倍

如果你想直接查看整个例子,下载并运行App样例然后选择缩放的例子。查看下列文件中的代码实现:

  • src/TouchHighlightImageButton.java(简单的helper类,当Image Button被按下它显示蓝色高亮)
  • src/ZoomActivity.java
  • layout/activity_zoom.xml

创建View

为想要缩放的内容创建一大一小两个版本布局文件。下面的例子为可点击的缩略图新建了一个ImageButton和一个ImageView来展示原图:

1. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
2. android:id="@+id/container"
3. android:layout_width="match_parent"
4. android:layout_height="match_parent">

6. <LinearLayout android:layout_width="match_parent"
7. android:layout_height="wrap_content"
8. android:orientation="vertical"
9. android:padding="16dp">

11. <ImageButton
12. android:id="@+id/thumb_button_1"
13. android:layout_width="100dp"
14. android:layout_height="75dp"
15. android:layout_marginRight="1dp"
16. android:src="@drawable/thumb1"
17. android:scaleType="centerCrop"
18. android:contentDescription="@string/description_image_1" />

20. </LinearLayout>

22. <!-- This initially-hidden ImageView will hold the expanded/zoomed version of
23. the images above. Without transformations applied, it takes up the entire
24. screen. To achieve the "zoom" animation, this view's bounds are animated
25. from the bounds of the thumbnail button above, to its final laid-out
26. bounds.
27. -->

29. <ImageView
30. android:id="@+id/expanded_image"
31. android:layout_width="match_parent"
32. android:layout_height="match_parent"
33. android:visibility="invisible"
34. android:contentDescription="@string/description_zoom_touch_close" />

36. </FrameLayout>

设置缩放动画

一旦实现了布局,我们需要设置触发缩放动画的事件handler。下面的例子为ImageButton添加了一个View.OnClickListener,当用户点击按钮时它执行放大动画。

1. public class ZoomActivity extends FragmentActivity {
2. // Hold a reference to the current animator,
3. // so that it can be canceled mid-way.
4. private Animator mCurrentAnimator;

6. // The system "short" animation time duration, in milliseconds. This
7. // duration is ideal for subtle animations or animations that occur
8. // very frequently.
9. private int mShortAnimationDuration;

11. @Override
12. protected void onCreate(Bundle savedInstanceState) {
13. super.onCreate(savedInstanceState);
14. setContentView(R.layout.activity_zoom);

16. // Hook up clicks on the thumbnail views.

18. final View thumb1View = findViewById(R.id.thumb_button_1);
19. thumb1View.setOnClickListener(new View.OnClickListener() {
20. @Override
21. public void onClick(View view) {
22. zoomImageFromThumb(thumb1View, R.drawable.image1);
23. }
24. });

26. // Retrieve and cache the system's default "short" animation time.
27. mShortAnimationDuration = getResources().getInteger(
28. android.R.integer.config_shortAnimTime);
29. }
30. ...
31. }

缩放View

我们现在需要适时应用放大动画了。通常来说,我们需要按边界来从小号View放大到大号View。下面的方法展示了如何实现缩放动画:

  1. 把高清图像资源设置到已经被隐藏的“放大版”的ImageView中。为表简单,下面的例子在UI线程中加载了一张大图。但是我们需要在一个单独的线程中来加载以免阻塞UI线程,然后再回到UI线程中设置。理想状况下,图片不要大过屏幕尺寸。
  2. 计算ImageView开始和结束时的边界。
  3. 从起始边到结束边同步地动态改变四个位置和大小属性X,Y(SCALE_X 和 SCALE_Y)。这四个动画被加入到了AnimatorSet,所以我们可以让它们一起开始。
  4. 缩小则运行相同的动画,但是是在用户点击屏幕放大时的逆向效果。我们可以在ImageView中添加一个View.OnClickListener来实现它。当点击时,ImageView缩回到原来缩略图的大小,然后设置它的visibility为GONE来隐藏。
1. private void zoomImageFromThumb(final View thumbView, int imageResId) {
2. // If there's an animation in progress, cancel it
3. // immediately and proceed with this one.
4. if (mCurrentAnimator != null) {
5. mCurrentAnimator.cancel();
6. }

8. // Load the high-resolution "zoomed-in" image.
9. final ImageView expandedImageView = (ImageView) findViewById(
10. R.id.expanded_image);
11. expandedImageView.setImageResource(imageResId);

13. // Calculate the starting and ending bounds for the zoomed-in image.
14. // This step involves lots of math. Yay, math.
15. final Rect startBounds = new Rect();
16. final Rect finalBounds = new Rect();
17. final Point globalOffset = new Point();

19. // The start bounds are the global visible rectangle of the thumbnail,
20. // and the final bounds are the global visible rectangle of the container
21. // view. Also set the container view's offset as the origin for the
22. // bounds, since that's the origin for the positioning animation
23. // properties (X, Y).
24. thumbView.getGlobalVisibleRect(startBounds);
25. findViewById(R.id.container)
26. .getGlobalVisibleRect(finalBounds, globalOffset);
27. startBounds.offset(-globalOffset.x, -globalOffset.y);
28. finalBounds.offset(-globalOffset.x, -globalOffset.y);

30. // Adjust the start bounds to be the same aspect ratio as the final
31. // bounds using the "center crop" technique. This prevents undesirable
32. // stretching during the animation. Also calculate the start scaling
33. // factor (the end scaling factor is always 1.0).
34. float startScale;
35. if ((float) finalBounds.width() / finalBounds.height()
36. > (float) startBounds.width() / startBounds.height()) {
37. // Extend start bounds horizontally
38. startScale = (float) startBounds.height() / finalBounds.height();
39. float startWidth = startScale * finalBounds.width();
40. float deltaWidth = (startWidth - startBounds.width()) / 2;
41. startBounds.left -= deltaWidth;
42. startBounds.right += deltaWidth;
43. } else {
44. // Extend start bounds vertically
45. startScale = (float) startBounds.width() / finalBounds.width();
46. float startHeight = startScale * finalBounds.height();
47. float deltaHeight = (startHeight - startBounds.height()) / 2;
48. startBounds.top -= deltaHeight;
49. startBounds.bottom += deltaHeight;
50. }

52. // Hide the thumbnail and show the zoomed-in view. When the animation
53. // begins, it will position the zoomed-in view in the place of the
54. // thumbnail.
55. thumbView.setAlpha(0f);
56. expandedImageView.setVisibility(View.VISIBLE);

58. // Set the pivot point for SCALE_X and SCALE_Y transformations
59. // to the top-left corner of the zoomed-in view (the default
60. // is the center of the view).
61. expandedImageView.setPivotX(0f);
62. expandedImageView.setPivotY(0f);

64. // Construct and run the parallel animation of the four translation and
65. // scale properties (X, Y, SCALE_X, and SCALE_Y).
66. AnimatorSet set = new AnimatorSet();
67. set
68. .play(ObjectAnimator.ofFloat(expandedImageView, View.X,
69. startBounds.left, finalBounds.left))
70. .with(ObjectAnimator.ofFloat(expandedImageView, View.Y,
71. startBounds.top, finalBounds.top))
72. .with(ObjectAnimator.ofFloat(expandedImageView, View.SCALE_X,
73. startScale, 1f)).with(ObjectAnimator.ofFloat(expandedImageView,
74. View.SCALE_Y, startScale, 1f));
75. set.setDuration(mShortAnimationDuration);
76. set.setInterpolator(new DecelerateInterpolator());
77. set.addListener(new AnimatorListenerAdapter() {
78. @Override
79. public void onAnimationEnd(Animator animation) {
80. mCurrentAnimator = null;
81. }

83. @Override
84. public void onAnimationCancel(Animator animation) {
85. mCurrentAnimator = null;
86. }
87. });
88. set.start();
89. mCurrentAnimator = set;

91. // Upon clicking the zoomed-in image, it should zoom back down
92. // to the original bounds and show the thumbnail instead of
93. // the expanded image.
94. final float startScaleFinal = startScale;
95. expandedImageView.setOnClickListener(new View.OnClickListener() {
96. @Override
97. public void onClick(View view) {
98. if (mCurrentAnimator != null) {
99. mCurrentAnimator.cancel();
100. }

102. // Animate the four positioning/sizing properties in parallel,
103. // back to their original values.
104. AnimatorSet set = new AnimatorSet();
105. set.play(ObjectAnimator
106. .ofFloat(expandedImageView, View.X, startBounds.left))
107. .with(ObjectAnimator
108. .ofFloat(expandedImageView,
109. View.Y,startBounds.top))
110. .with(ObjectAnimator
111. .ofFloat(expandedImageView,
112. View.SCALE_X, startScaleFinal))
113. .with(ObjectAnimator
114. .ofFloat(expandedImageView,
115. View.SCALE_Y, startScaleFinal));
116. set.setDuration(mShortAnimationDuration);
117. set.setInterpolator(new DecelerateInterpolator());
118. set.addListener(new AnimatorListenerAdapter() {
119. @Override
120. public void onAnimationEnd(Animator animation) {
121. thumbView.setAlpha(1f);
122. expandedImageView.setVisibility(View.GONE);
123. mCurrentAnimator = null;
124. }

126. @Override
127. public void onAnimationCancel(Animator animation) {
128. thumbView.setAlpha(1f);
129. expandedImageView.setVisibility(View.GONE);
130. mCurrentAnimator = null;
131. }
132. });
133. set.start();
134. mCurrentAnimator = set;
135. }
136. });
137. }