展示Card翻转动画

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

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

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





如果你想直接查看整个例子,下载并运行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" />
  8. <!-- Rotate. -->
  9. <objectAnimator
  10. android:valueFrom="-180"
  11. android:valueTo="0"
  12. android:propertyName="rotationY"
  13. android:interpolator="@android:interpolator/accelerate_decelerate"
  14. android:duration="@integer/card_flip_time_full" />
  15. <!-- Half-way through the rotation (see startOffset), set the alpha to 1. -->
  16. <objectAnimator
  17. android:valueFrom="0.0"
  18. android:valueTo="1.0"
  19. android:propertyName="alpha"
  20. android:startOffset="@integer/card_flip_time_half"
  21. android:duration="1" />
  22. </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" />
  9. <!-- Half-way through the rotation (see startOffset), set the alpha to 0. -->
  10. <objectAnimator
  11. android:valueFrom="1.0"
  12. android:valueTo="0.0"
  13. android:propertyName="alpha"
  14. android:startOffset="@integer/card_flip_time_half"
  15. android:duration="1" />
  16. </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" />
  8. <!-- Rotate. -->
  9. <objectAnimator
  10. android:valueFrom="180"
  11. android:valueTo="0"
  12. android:propertyName="rotationY"
  13. android:interpolator="@android:interpolator/accelerate_decelerate"
  14. android:duration="@integer/card_flip_time_full" />
  15. <!-- Half-way through the rotation (see startOffset), set the alpha to 1. -->
  16. <objectAnimator
  17. android:valueFrom="0.0"
  18. android:valueTo="1.0"
  19. android:propertyName="alpha"
  20. android:startOffset="@integer/card_flip_time_half"
  21. android:duration="1" />
  22. </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" />
  9. <!-- Half-way through the rotation (see startOffset), set the alpha to 0. -->
  10. <objectAnimator
  11. android:valueFrom="1.0"
  12. android:valueTo="0.0"
  13. android:propertyName="alpha"
  14. android:startOffset="@integer/card_flip_time_half"
  15. android:duration="1" />
  16. </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">
  8. <TextView android:id="@android:id/text1"
  9. style="?android:textAppearanceLarge"
  10. android:textStyle="bold"
  11. android:textColor="#fff"
  12. android:layout_width="match_parent"
  13. android:layout_height="wrap_content"
  14. android:text="@string/card_back_title" />
  15. <TextView style="?android:textAppearanceSmall"
  16. android:textAllCaps="true"
  17. android:textColor="#80ffffff"
  18. android:textStyle="bold"
  19. android:lineSpacingMultiplier="1.2"
  20. android:layout_width="match_parent"
  21. android:layout_height="wrap_content"
  22. android:text="@string/card_back_description" />
  23. </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. }
  13. /**
  14. * A fragment representing the back of the card.
  15. */
  16. public class CardBackFragment extends Fragment {
  17. @Override
  18. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  19. Bundle savedInstanceState) {
  20. return inflater.inflate(R.layout.fragment_card_back, container, false);
  21. }
  22. }
  23. }

应用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 {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.activity_activity_card_flip);
  6. if (savedInstanceState == null) {
  7. getFragmentManager()
  8. .beginTransaction()
  9. .add(R.id.container, new CardFrontFragment())
  10. .commit();
  11. }
  12. }
  13. ...
  14. }

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

  • 将Fragment转换设置我们刚做的自定义动画

  • 用新Fragment替换当前显示的Fragment,并且应用之前创建的动画到该事件中。

  • 添加之前显示的Fragment到Fragment的回退栈(back stack)中,所以当用户按下 Back 键时,Card会翻转回来。

  1. private void flipCard() {
  2. if (mShowingBack) {
  3. getFragmentManager().popBackStack();
  4. return;
  5. }
  6. // Flip to the back.
  7. mShowingBack = true;
  8. // Create and commit a new fragment transaction that adds the fragment for the back of
  9. // the card, uses custom animations, and is part of the fragment manager's back stack.
  10. getFragmentManager()
  11. .beginTransaction()
  12. // Replace the default fragment animations with animator resources representing
  13. // rotations when switching to the back of the card, as well as animator
  14. // resources representing rotations when flipping back to the front (e.g. when
  15. // the system Back button is pressed).
  16. .setCustomAnimations(
  17. R.animator.card_flip_right_in, R.animator.card_flip_right_out,
  18. R.animator.card_flip_left_in, R.animator.card_flip_left_out)
  19. // Replace any fragments currently in the container view with a fragment
  20. // representing the next page (indicated by the just-incremented currentPage
  21. // variable).
  22. .replace(R.id.container, new CardBackFragment())
  23. // Add this transaction to the back stack, allowing users to press Back
  24. // to get to the front of the card.
  25. .addToBackStack(null)
  26. // Commit the transaction.
  27. .commit();
  28. }