• 7.7 Android动画特效" level="2">7.7 Android动画特效
    • 7.7.1 灵动菜单" level="3">7.7.1 灵动菜单
    • 7.7.2 计时器动画" level="3">7.7.2 计时器动画
    • 7.7.3 下拉展开动画" level="3">7.7.3 下拉展开动画

    7.7 Android动画特效" class="reference-link">7.7 Android动画特效

    通过前面的学习,我们已经基本掌握了如何在Android中使用各种不同的动画来实现各种酷炫的效果。然而在实际项目中,各种优秀的UI设计光靠程序员是远远不够的,一个靠谱的美工也是非常重要的。

    下面给大家列举一些动画效果的实例,让大家熟悉如何通过Android的动画框架来创建赏心悦目的动画。

    7.7.1 灵动菜单" class="reference-link">7.7.1 灵动菜单

    图7.21中展示的是灵动菜单的效果图

    7.7 Android动画特效 - 图1 图7.21 灵动菜单

    当用户点击小红点后,弹出菜单,并带有一个缓冲的过渡动画,这也是Google在Material Design中所强调的动画过渡效果,如图7.22所示。

    7.7 Android动画特效 - 图2 图7.22 菜单弹出效果

    那么这样一个动画效果的菜单是怎么实现的呢?首先,它具有用户交互性,所以肯定不能使用视图动画而必须使用属性动画。其次,只需要针对每个不同的按钮设置不同的动画,并设置相应的差值器就可以实现展开、合拢效果了。理清思路后,实现就比较简单了,这里以展开动画为例,代码如下所示。

    1. private void startAnim() {
    2. ObjectAnimator animator0 = ObjectAnimator.ofFloat(
    3. mImageViews.get(0),
    4. "alpha",
    5. 1F,
    6. 0.5F);
    7. ObjectAnimator animator1 = ObjectAnimator.ofFloat(
    8. mImageViews.get(1),
    9. "translationY",
    10. 200F);
    11. ObjectAnimator animator2 = ObjectAnimator.ofFloat(
    12. mImageViews.get(2),
    13. "translationX",
    14. 200F);
    15. ObjectAnimator animator3 = ObjectAnimator.ofFloat(
    16. mImageViews.get(3),
    17. "translationY",
    18. -200F);
    19. ObjectAnimator animator4 = ObjectAnimator.ofFloat(
    20. mImageViews.get(4),
    21. "translationX",
    22. -200F);
    23. AnimatorSet set = new AnimatorSet();
    24. set.setDuration(500);
    25. set.setInterpolator(new BounceInterpolator());
    26. set.playTogether(
    27. animator0,
    28. animator1,
    29. animator2,
    30. animator3,
    31. animator4);
    32. set.start();
    33. mFlag = false;
    34. }

    下面再为按钮设置点击事件即可完成整个功能。

    1. @Override
    2. public void onClick(View v) {
    3.  
    4. switch (v.getId()) {
    5. case R.id.imageView_a:
    6. if (mFlag) {
    7. startAnim();
    8. } else {
    9. closeAnim();
    10. }
    11. break;
    12. default:
    13. Toast.makeText(PropertyTest.this, "" + v.getId(),
    14. Toast.LENGTH_SHORT).show();
    15. break;
    16. }
    17. }

    7.7.2 计时器动画" class="reference-link">7.7.2 计时器动画

    通过这个实例,我们来熟悉一下ValueAnimator的使用,要实现计时器的动画效果,方法有很多,这里只是为了演示ValueAnimator的效果,因而使用ValueAnimator来实现,程序运行效果如图7.23所示。

    7.7 Android动画特效 - 图3 图7.23 计时器

    当用户点击后,数字会不断增加,如图7.24所示。

    7.7 Android动画特效 - 图4 图7.24 计时器增加

    要完成以上两个效果是比较简单的,只需要借助ValueAnimator来实现数字的不断增加,并将值设置给TextView即可,代码如下所示。

    1. public void tvTimer(final View view) {
    2. ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 100);
    3. valueAnimator.addUpdateListener(
    4. new ValueAnimator.AnimatorUpdateListener() {
    5. @Override
    6. public void onAnimationUpdate(ValueAnimator animation) {
    7. ((TextView) view).setText("$ " +
    8. (Integer) animation.getAnimatedValue());
    9. }
    10.  
    11. });
    12. valueAnimator.setDuration(3000);
    13. valueAnimator.start();
    14. }

    7.7.3 下拉展开动画" class="reference-link">7.7.3 下拉展开动画

    下面再来演示一个ValueAnimator的小例子,这个例子来源于一个群友的问题,他希望能实现一个这样的效果:当点击一个View的时候,显示下面隐藏的一个View,要实现这个功能,需要将View的visibility属性由gone设置为visible即可,但是这个过程是瞬间完成的,如何让View在显示时增加一个动画效果呢?要实现这样的效果,需要让隐藏的View的高度不断发生变化,但不是迅速增大到目标值。所以使用ValueAnimator来模拟这个过程。首先,写一个简单的布局,两个LinearLayout,一个显示,一个隐藏。

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    3.  
    4. android:layout_width="match_parent"
    5. android:layout_height="wrap_content"
    6. android:orientation="vertical">
    7.  
    8. <LinearLayout
    9. android:layout_width="fill_parent"
    10. android:layout_height="wrap_content"
    11. android:gravity="center_vertical"
    12. android:onClick="llClick"
    13. android:background="@android:color/holo_blue_bright"
    14. android:orientation="horizontal">
    15.  
    16. <ImageView
    17. android:id="@+id/app_icon"
    18. android:layout_width="wrap_content"
    19. android:layout_height="wrap_content"
    20. android:layout_gravity="center"
    21. android:src="@drawable/ic_launcher" />
    22.  
    23. <TextView
    24. android:layout_width="wrap_content"
    25. android:layout_height="wrap_content"
    26. android:layout_marginLeft="5dp"
    27. android:gravity="left"
    28. android:text="Click Me"
    29. android:textSize="30sp" />
    30. </LinearLayout>
    31.  
    32. <LinearLayout
    33. android:id="@+id/hidden_view"
    34. android:layout_width="match_parent"
    35. android:layout_height="40dp"
    36. android:background="@android:color/holo_orange_light"
    37. android:gravity="center_vertical"
    38. android:orientation="horizontal"
    39. android:visibility="gone">
    40.  
    41. <ImageView
    42. android:src="@drawable/ic_launcher"
    43. android:layout_width="wrap_content"
    44. android:layout_height="wrap_content"
    45. android:layout_gravity="center" />
    46.  
    47. <TextView
    48. android:id="@+id/tv_hidden"
    49. android:layout_width="wrap_content"
    50. android:layout_height="match_parent"
    51. android:gravity="center"
    52. android:textSize="20sp"
    53. android:text="I am hidden" />
    54. </LinearLayout>
    55. </LinearLayout>

    为了区分两个不同的LinearLayout,我们给它们设置了不同的背景颜色和显示文字。接下来,当点击上面的LinearLayout时,需要获取到隐藏的LinearLayout最终需要到达的一个高度,即我们的目标值,通过将布局文件中的dp值转化为像素值即可。

    1. mDensity = getResources().getDisplayMetrics().density;
    2. mHiddenViewMeasuredHeight = (int) (mDensity * 40 + 0.5);

    40就是在XML文件中定义的布局高度。

    然后给这个过程增加一个动画效果,前面分析了,需要使用ValueAnimator来创建一个从0到目标值的数值发生器,并由此来改变View的布局属性。

    1. ValueAnimator animator = ValueAnimator.ofInt(start, end);
    2. animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    3.  
    4. @Override
    5. public void onAnimationUpdate(ValueAnimator valueAnimator) {
    6. int value = (Integer) valueAnimator.getAnimatedValue();
    7. ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
    8. layoutParams.height = value;
    9. view.setLayoutParams(layoutParams);
    10. }
    11. });

    通过这样一个简单的ValueAnimator,就可以非常方便地实现显示、隐藏的动画效果了,完整代码如下所示。

    1. package com.imooc.anim;
    2.  
    3. import android.animation.Animator;
    4. import android.animation.AnimatorListenerAdapter;
    5. import android.animation.ValueAnimator;
    6. import android.app.Activity;
    7. import android.os.Bundle;
    8. import android.view.View;
    9. import android.view.ViewGroup;
    10. import android.widget.LinearLayout;
    11.  
    12. public class DropTest extends Activity {
    13.  
    14. private LinearLayout mHiddenView;
    15. private float mDensity;
    16. private int mHiddenViewMeasuredHeight;
    17.  
    18. @Override
    19. protected void onCreate(Bundle savedInstanceState) {
    20. super.onCreate(savedInstanceState);
    21. setContentView(R.layout.drop);
    22. mHiddenView = (LinearLayout) findViewById(R.id.hidden_view);
    23. //获取像素密度
    24. mDensity = getResources().getDisplayMetrics().density;
    25.  
    26. //获取布局的高度
    27. mHiddenViewMeasuredHeight = (int) (mDensity * 40 + 0.5);
    28. }
    29.  
    30. public void llClick(View view) {
    31. if (mHiddenView.getVisibility() == View.GONE) {
    32. //打开动画
    33. animateOpen(mHiddenView);
    34. } else {
    35. //关闭动画
    36. animateClose(mHiddenView);
    37. }
    38. }
    39.  
    40. private void animateOpen(final View view) {
    41. view.setVisibility(View.VISIBLE);
    42. ValueAnimator animator = createDropAnimator(
    43. view,
    44. 0,
    45. mHiddenViewMeasuredHeight);
    46. animator.start();
    47. }
    48. private void animateClose(final View view) {
    49. int origHeight = view.getHeight();
    50. ValueAnimator animator = createDropAnimator(view, origHeight, 0);
    51. animator.addListener(new AnimatorListenerAdapter() {
    52. public void onAnimationEnd(Animator animation) {
    53. view.setVisibility(View.GONE);
    54. }
    55. });
    56. animator.start();
    57. }
    58.  
    59. private ValueAnimator createDropAnimator(
    60. final View view, int start, int end) {
    61. ValueAnimator animator = ValueAnimator.ofInt(start, end);
    62. animator.addUpdateListener(
    63. new ValueAnimator.AnimatorUpdateListener() {
    64.  
    65. @Override
    66. public void onAnimationUpdate(ValueAnimator valueAnimator) {
    67. int value = (Integer) valueAnimator.getAnimatedValue();
    68. ViewGroup.LayoutParams layoutParams =
    69. view.getLayoutParams();
    70. layoutParams.height = value;
    71. view.setLayoutParams(layoutParams);
    72. }
    73. });
    74. return animator;
    75. }
    76. }

    程序运行效果如图7.25所示。

    7.7 Android动画特效 - 图5 图7.25 程序运行初始状态

    当点击View时,会逐渐弹出下面隐藏的View,如图7.26所示。

    7.7 Android动画特效 - 图6 图7.26 点击显示隐藏View