展示Card翻转动画

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

这节课展示如何使用自定义Fragment动画实现Card翻转动画。Card翻转动画通过模拟Card翻转的效果实现view内容的切换。

下面是card翻转动画的样子:

0.7 倍 1.0 倍 1.2 倍 1.5 倍 2.0 倍

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

  • src/CardFlipActivity.java
  • animator/card_flip_right_in.xml
  • animator/card_flip_right_out.xml
  • animator/card_flip_left_in.xml
  • animator/card_flip_left_out.xml
  • layout/fragment_card_back.xml
  • layout/fragment_card_front.xml

创建Animator

创建Card翻转动画,我们需要两个Animator。一个让正面的card的右侧向左翻转渐出,一个让背面的Card向右翻转渐入。我们还需要两个 Animator让背面的card的右侧向左翻转渐入,一个让向右翻转渐入。

card_flip_left_in.xml

1. <set xmlns:android="http://schemas.android.com/apk/res/android">
2. <!-- Before rotating, immediately set the alpha to 0. -->
3. <objectAnimator
4. android:valueFrom="1.0"
5. android:valueTo="0.0"
6. android:propertyName="alpha"
7. android:duration="0" />

9. <!-- Rotate. -->
10. <objectAnimator
11. android:valueFrom="-180"
12. android:valueTo="0"
13. android:propertyName="rotationY"
14. android:interpolator="@android:interpolator/accelerate_decelerate"
15. android:duration="@integer/card_flip_time_full" />

17. <!-- Half-way through the rotation (see startOffset), set the alpha to 1. -->
18. <objectAnimator
19. android:valueFrom="0.0"
20. android:valueTo="1.0"
21. android:propertyName="alpha"
22. android:startOffset="@integer/card_flip_time_half"
23. android:duration="1" />
24. </set>

card_flip_left_out.xml

1. <set xmlns:android="http://schemas.android.com/apk/res/android">
2. <!-- Rotate. -->
3. <objectAnimator
4. android:valueFrom="0"
5. android:valueTo="180"
6. android:propertyName="rotationY"
7. android:interpolator="@android:interpolator/accelerate_decelerate"
8. android:duration="@integer/card_flip_time_full" />

10. <!-- Half-way through the rotation (see startOffset), set the alpha to 0. -->
11. <objectAnimator
12. android:valueFrom="1.0"
13. android:valueTo="0.0"
14. android:propertyName="alpha"
15. android:startOffset="@integer/card_flip_time_half"
16. android:duration="1" />
17. </set>

card_flip_right_in.xml

1. <set xmlns:android="http://schemas.android.com/apk/res/android">
2. <!-- Before rotating, immediately set the alpha to 0. -->
3. <objectAnimator
4. android:valueFrom="1.0"
5. android:valueTo="0.0"
6. android:propertyName="alpha"
7. android:duration="0" />

9. <!-- Rotate. -->
10. <objectAnimator
11. android:valueFrom="180"
12. android:valueTo="0"
13. android:propertyName="rotationY"
14. android:interpolator="@android:interpolator/accelerate_decelerate"
15. android:duration="@integer/card_flip_time_full" />

17. <!-- Half-way through the rotation (see startOffset), set the alpha to 1. -->
18. <objectAnimator
19. android:valueFrom="0.0"
20. android:valueTo="1.0"
21. android:propertyName="alpha"
22. android:startOffset="@integer/card_flip_time_half"
23. android:duration="1" />
24. </set>

card_flip_right_out.xml

1. <set xmlns:android="http://schemas.android.com/apk/res/android">
2. <!-- Rotate. -->
3. <objectAnimator
4. android:valueFrom="0"
5. android:valueTo="-180"
6. android:propertyName="rotationY"
7. android:interpolator="@android:interpolator/accelerate_decelerate"
8. android:duration="@integer/card_flip_time_full" />

10. <!-- Half-way through the rotation (see startOffset), set the alpha to 0. -->
11. <objectAnimator
12. android:valueFrom="1.0"
13. android:valueTo="0.0"
14. android:propertyName="alpha"
15. android:startOffset="@integer/card_flip_time_half"
16. android:duration="1" />
17. </set>

创建View

Card的每一面是一个独立的布局,比如两屏文字,两张图片,或者任何View的组合。然后我们将在应用动画的Fragment里面用到这两个布局。下面的布局创建了展示文本Card的一面:

1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
2. android:layout_width="match_parent"
3. android:layout_height="match_parent"
4. android:orientation="vertical"
5. android:background="#a6c"
6. android:padding="16dp"
7. android:gravity="bottom">

9. <TextView android:id="@android:id/text1"
10. style="?android:textAppearanceLarge"
11. android:textStyle="bold"
12. android:textColor="#fff"
13. android:layout_width="match_parent"
14. android:layout_height="wrap_content"
15. android:text="@string/card_back_title" />

17. <TextView style="?android:textAppearanceSmall"
18. android:textAllCaps="true"
19. android:textColor="#80ffffff"
20. android:textStyle="bold"
21. android:lineSpacingMultiplier="1.2"
22. android:layout_width="match_parent"
23. android:layout_height="wrap_content"
24. android:text="@string/card_back_description" />

26. </LinearLayout>

Card另一面显示一个 ImageView:

1. <ImageView xmlns:android="http://schemas.android.com/apk/res/android"
2. android:layout_width="match_parent"
3. android:layout_height="match_parent"
4. android:src="@drawable/image1"
5. android:scaleType="centerCrop"
6. android:contentDescription="@string/description_image_1" />

创建Fragment

为Card正反面创建Fragment,这些类从 onCreateView() 方法中分别为每个Framgent返回你之前创建的布局。在想要显示Card的父Activity中,我们可以创建对应的Fragment 实例。下面的例子展示父Activity内嵌套的Fragment:

1. public class CardFlipActivity extends Activity {
2. ...
3. /**
4. * A fragment representing the front of the card.
5. */
6. public class CardFrontFragment extends Fragment {
7. @Override
8. public View onCreateView(LayoutInflater inflater, ViewGroup container,
9. Bundle savedInstanceState) {
10. return inflater.inflate(R.layout.fragment_card_front, container, false);
11. }
12. }

14. /**
15. * A fragment representing the back of the card.
16. */
17. public class CardBackFragment extends Fragment {
18. @Override
19. public View onCreateView(LayoutInflater inflater, ViewGroup container,
20. Bundle savedInstanceState) {
21. return inflater.inflate(R.layout.fragment_card_back, container, false);
22. }
23. }
24. }

应用card翻转动画

现在,我们需要在父Activity中展示Fragment。为此,首先创建Activity的布局。下面例子创建了一个可以在运行时添加Fragment的 FrameLayout。

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" />

在Activity代码中,把先前创建的布局设置成其ContentVew。当Activity创建时展示一个默认的Fragment是个不错的注意。所以下面的Activity样例表明了如何默认显示卡片正面:

1. public class CardFlipActivity extends Activity {

3. @Override
4. protected void onCreate(Bundle savedInstanceState) {
5. super.onCreate(savedInstanceState);
6. setContentView(R.layout.activity_activity_card_flip);

8. if (savedInstanceState == null) {
9. getFragmentManager()
10. .beginTransaction()
11. .add(R.id.container, new CardFrontFragment())
12. .commit();
13. }
14. }
15. ...
16. }

既然现在显示了卡片的正面,我们可以在合适时机用翻转动画显示卡片背面了。创建一个方法来显示背面,它需要做下面这些事情:

  • 将Fragment转换设置我们刚做的自定义动画
  • 用新Fragment替换当前显示的Fragment,并且应用之前创建的动画到该事件中。
  • 添加之前显示的Fragment到Fragment的回退栈(back stack)中,所以当用户按下 Back 键时,Card会翻转回来。
1. private void flipCard() {
2. if (mShowingBack) {
3. getFragmentManager().popBackStack();
4. return;
5. }

7. // Flip to the back.

9. mShowingBack = true;

11. // Create and commit a new fragment transaction that adds the fragment for the back of
12. // the card, uses custom animations, and is part of the fragment manager's back stack.

14. getFragmentManager()
15. .beginTransaction()

17. // Replace the default fragment animations with animator resources representing
18. // rotations when switching to the back of the card, as well as animator
19. // resources representing rotations when flipping back to the front (e.g. when
20. // the system Back button is pressed).
21. .setCustomAnimations(
22. R.animator.card_flip_right_in, R.animator.card_flip_right_out,
23. R.animator.card_flip_left_in, R.animator.card_flip_left_out)

25. // Replace any fragments currently in the container view with a fragment
26. // representing the next page (indicated by the just-incremented currentPage
27. // variable).
28. .replace(R.id.container, new CardBackFragment())

30. // Add this transaction to the back stack, allowing users to press Back
31. // to get to the front of the card.
32. .addToBackStack(null)

34. // Commit the transaction.
35. .commit();
36. }