• 12.8 Material Design动画效果" level="2">12.8 Material Design动画效果
    • 12.8.1 Ripple效果" level="3">12.8.1 Ripple效果
    • 12.8.2 Circular Reveal" level="3">12.8.2 Circular Reveal
    • 12.8.3 View state changes Animation" level="3">12.8.3 View state changes Animation

    12.8 Material Design动画效果" class="reference-link">12.8 Material Design动画效果

    动画已经成了UI设计中一个非常重要的组成部分,在Android 5.X的UI设计Material Design中,更是使用了大量的动画效果,同时Google也在官方设计文档上增加了对动画的设计指导。

    12.8.1 Ripple效果" class="reference-link">12.8.1 Ripple效果

    在Android 5.X中,Material Design大量使用了Ripple效果,即点击后的波纹效果。可以通过如下代码设置波纹的背景。

    1. //波纹有边界
    2. android:background="?android:attr/selectableItemBackground"
    3. //波纹超出边界
    4. android:background="?android:attr/selectableItemBackgroundBorderless"

    波纹有边界是指波纹被限制在控件的边界中,而波纹超出边界则是波纹不会限制在控件边界中,会呈圆形发散出去,下面通过一个实例,演示一下Android 5.X的波纹效果,XML代码如下所示。

    1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    2. xmlns:tools="http://schemas.android.com/tools"
    3. android:layout_width="match_parent"
    4. android:layout_height="match_parent"
    5. android:background="@android:color/holo_blue_bright">
    6.  
    7. <Button
    8. android:layout_width="100dp"
    9. android:layout_height="100dp"
    10. android:background="?android:attr/selectableItemBackground"
    11. android:text="有界波纹"
    12. android:textColor="@android:color/white" />
    13.  
    14. <Button
    15. android:layout_width="100dp"
    16. android:layout_height="100dp"
    17. android:background="?android:attr/selectableItemBackground-Borderless"
    18. android:textColor="@android:color/white"
    19. android:text="无界波纹" />
    20. </LinearLayout>

    显示效果如图12.19、图12.20所示。

    12.8 Material Design动画效果 - 图1 12.8 Material Design动画效果 - 图2
    图12.19 波纹效果1 图12.20 波纹效果2

    同样,你也可以在XML文件中直接来创建一个具有Ripple效果的XML文件,代码如下所示。

    1. <ripple
    2. xmlns:android="http://schemas.android.com/apk/res/android"
    3. android:color="@android:color/holo_blue_bright">
    4. <item>
    5. <shape
    6. android:shape="oval">
    7. <solid android:color="?android:colorAccent" />
    8. </shape>
    9. </item>
    10. </ripple>

    使用方法如下所示,与使用一般的XML资源方法相同。

    1. <Button
    2. android:layout_width="100dp"
    3. android:layout_height="100dp"
    4. android:background="@drawable/ripple" />

    显示效果如图12.21所示。

    12.8 Material Design动画效果 - 图3 图12.21 波纹效果</h4>

    12.8.2 Circular Reveal" class="reference-link">12.8.2 Circular Reveal

    这个动画效果在Google IO大会的演示视频中出现了很多次,具体表现为一个View以圆形的形式展开、揭示出来。通过ViewAnimationUtils.createCircularReveal()方法可以创建一个RevealAnimator动画,代码如下所示。

    1. public static Animator createCircularReveal(
    2. View view,
    3. int centerX,
    4. int centerY,
    5. float startRadius,
    6. float endRadius) {
    7. return new RevealAnimator(view, centerX, centerY, startRadius, endRadius);
    8. }

    RevealAnimator的使用非常简单,主要是设置几个关键的坐标点:

    • centerX动画开始的中心点X
    • centerY动画开始的中心点Y
    • startRadius动画开始半径
    • startRadius动画结束半径

    通过下面的例子,可以非常直观地感受到这种动画效果,XML代码如下所示。

    1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    2. xmlns:tools="http://schemas.android.com/tools"
    3. android:layout_width="match_parent"
    4. android:layout_height="match_parent"
    5. android:orientation="vertical"
    6. tools:context=".MainActivity">
    7.  
    8. <ImageView
    9. android:id="@+id/oval"
    10. android:layout_width="100dp"
    11. android:layout_height="100dp"
    12. android:background="@drawable/oval" />
    13.  
    14. < ImageView
    15. android:id="@+id/rect"
    16. android:layout_width="100dp"
    17. android:layout_height="100dp"
    18. android:background="@drawable/rect" />
    19. </LinearLayout>

    程序代码如下所示。

    1. package com.xys.myapplication;
    2.  
    3. import android.animation.Animator;
    4.  
    5. import android.app.Activity;
    6. import android.os.Bundle;
    7. import android.view.View;
    8. import android.view.ViewAnimationUtils;
    9. import android.view.animation.AccelerateDecelerateInterpolator;
    10. import android.view.animation.AccelerateInterpolator;
    11.  
    12. import com.imooc.myapplication.R;
    13.  
    14. public class MainActivity extends Activity {
    15.  
    16. @Override
    17. protected void onCreate(Bundle savedInstanceState) {
    18. super.onCreate(savedInstanceState);
    19. setContentView(R.layout.activity_main);
    20. final View oval = this.findViewById(R.id.oval);
    21. oval.setOnClickListener(new View.OnClickListener() {
    22. @Override
    23. public void onClick(View v) {
    24. Animator animator =
    25. ViewAnimationUtils.createCircularReveal(
    26. oval,
    27. oval.getWidth() / 2,
    28. oval.getHeight() / 2,
    29. oval.getWidth(),
    30. 0);
    31. animator.setInterpolator(
    32. new AccelerateDecelerateInterpolator());
    33. animator.setDuration(2000);
    34. animator.start();
    35. }
    36. });
    37. final View rect = this.findViewById(R.id.rect);
    38.  
    39. rect.setOnClickListener(new View.OnClickListener() {
    40. @Override
    41. public void onClick(View v) {
    42. Animator animator =
    43. ViewAnimationUtils.createCircularReveal(
    44. rect,
    45. 0,
    46. 0,
    47. 0,
    48. (float) Math.hypot(rect.getWidth(),
    49. rect.getHeight()));
    50. animator.setInterpolator(
    51. new AccelerateInterpolator());
    52. animator.setDuration(2000);
    53. animator.start();
    54. }
    55. });
    56. }
    57. }

    以上程序设置了两种形式,通过设置不同的坐标值,改变圆形展开的方式和效果,如图12.22、图12.23所示。

    12.8 Material Design动画效果 - 图4 12.8 Material Design动画效果 - 图5
    图12.22 CircularReveal(中心) 图12.23 CircularReveal(边界)

    12.8.3 View state changes Animation" class="reference-link">12.8.3 View state changes Animation

    在Android 5.X中,系统提供了视图状态改变来设置一个视图的状态切换动画。

    • StateListAnimator

    StateListAnimator作为视图改变时的动画效果,通常会使用Selector来进行设置,但以前设置Selector的时候,通常是修改背景来达到反馈的效果。现在,在Android 5.X中,可以使用动画来作为视图改变的效果。

    在XML中定义一个StateListAnimator,并添加到Selector中,代码如下所示。

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <selector xmlns:android="http://schemas.android.com/apk/res/android">
    3. <item android:state_pressed="true">
    4. <set>
    5. <objectAnimator android:propertyName="rotationX"
    6. android:duration="@android:integer/config_shortAnimTime"
    7. android:valueTo="360"
    8. android:valueType="floatType"/>
    9. </set>
    10. </item>
    11. <item android:state_pressed="false">
    12. <set>
    13. <objectAnimator android:propertyName="rotationX"
    14. android:duration="@android:integer/config_shortAnimTime"
    15. android:valueTo="0"
    16. android:valueType="floatType"/>
    17. </set>
    18. </item>
    19. </selector>

    在一般的XML布局中,使用如下代码来将StateListAnimator添加给一个视图。

    1. <Button
    2. android:layout_width="200dp"
    3. android:layout_height="200dp"
    4. android:stateListAnimator="@drawable/anim_change"/>

    同样,在代码中也可以调用AnimationInflater.loadStateListAnimator()方法,并且通过View.setStateListAnimator()方法分配动画到视图上,效果如图12.24所示。

    12.8 Material Design动画效果 - 图6 图12.24 StateListAnimator

    • animated-selector

    animated-selector同样是一个状态改变的动画效果Selector。在Android 5.0中,很多Material Design的控件设计,都是通过这种方式来实现的,例如我们熟悉的check_box的动画效果,就是使用类似帧动画的切换效果,模拟进行点击时的切换效果,如图12.25所示。

    12.8 Material Design动画效果 - 图7 图12.25 check_box状态切换图

    下面就仿照这个例子来实现一个具有动画效果的状态切换按钮。首先需要一组类似图12.25的状态切换图,如图12.26所示。

    12.8 Material Design动画效果 - 图8 图12.26 状态切换图

    有了这样一组图,就可以在XML文件中定义animated-selector。animated- selector与selector的使用十分类似,同样是通过<item>标签来区分不同的状态,代码如下所示。

    1. <item
    2. android:id="@+id/state_on"
    3. android:state_checked="true">
    4. <bitmap android:src="@drawable/ic_done_anim_000" />
    5. </item>
    6. <item android:id="@+id/state_off">
    7. <bitmap android:src="@drawable/ic_plus_anim_030" />
    8. </item>

    图ic_done_anim_000与图ic_plus_anim_030分别代表两种不同的状态。同时,我们也给这两种状态增加了ID来进行区分,下面就可以使用<transition>标签来给这两种状态设置不同的过渡图片,这点非常类似Android中的帧动画效果,完整代码如下所示。

    1. <animated-selector xmlns:android="http://schemas.android.com/apk/
    2. res/android">
    3. <item
    4. android:id="@+id/state_on"
    5. android:state_checked="true">
    6. <bitmap android:src="@drawable/ic_done_anim_030" />
    7. </item>
    8. <item android:id="@+id/state_off">
    9. <bitmap android:src="@drawable/ic_plus_anim_030" />
    10. </item>
    11. <transition
    12. android:fromId="@+id/state_on"
    13. android:toId="@+id/state_off">
    14. <animation-list>
    15. <item android:duration="16">
    16. <bitmap android:src="@drawable/ic_plus_anim_000" />
    17. </item>
    18. <item android:duration="16">
    19. <bitmap android:src="@drawable/ic_plus_anim_001" />
    20. </item>
    21. <item android:duration="16">
    22. <bitmap android:src="@drawable/ic_plus_anim_002" />
    23. </item>
    24. <item android:duration="16">
    25. <bitmap android:src="@drawable/ic_plus_anim_003" />
    26. </item>
    27. ……
    28. <item android:duration="16">
    29. <bitmap android:src="@drawable/ic_plus_anim_028" />
    30. </item>
    31. <item android:duration="16">
    32. <bitmap android:src="@drawable/ic_plus_anim_029" />
    33. </item>
    34. <item android:duration="16">
    35. <bitmap android:src="@drawable/ic_plus_anim_030" />
    36. </item>
    37. </animation-list>
    38.  
    39. </transition>
    40. <transition
    41. android:fromId="@+id/state_off"
    42. android:toId="@+id/state_on">
    43. <animation-list>
    44. <item android:duration="16">
    45. <bitmap android:src="@drawable/ic_done_anim_000" />
    46. </item>
    47. <item android:duration="16">
    48. <bitmap android:src="@drawable/ic_done_anim_001" />
    49. </item>
    50. <item android:duration="16">
    51. <bitmap android:src="@drawable/ic_done_anim_002" />
    52. </item>
    53. <item android:duration="16">
    54. <bitmap android:src="@drawable/ic_done_anim_003" />
    55. </item>
    56. ……
    57. <item android:duration="16">
    58. <bitmap android:src="@drawable/ic_done_anim_028" />
    59. </item>
    60. <item android:duration="16">
    61. <bitmap android:src="@drawable/ic_done_anim_029" />
    62. </item>
    63. <item android:duration="16">
    64. <bitmap android:src="@drawable/ic_done_anim_030" />
    65. </item>
    66. </animation-list>
    67. </transition>
    68. </animated-selector>

    有了animated-selector之后,只需要把它应用到一个ImageView上即可。同时,在代码中设置不同的点击状态。在Android中,通常使用如下所示的系统属性来设置切换状态。

    1. private static final int[] STATE_CHECKED = new int[]{
    2. android.R.attr.state_checked};
    3. private static final int[] STATE_UNCHECKED = new int[]{};

    当点击时,通过setImageState方法来改变一个背景状态图,完整代码如下所示。

    1. package com.imooc.animatedselector;
    2.  
    3. import android.app.Activity;
    4. import android.graphics.drawable.Drawable;
    5. import android.os.Bundle;
    6. import android.view.View;
    7. import android.widget.ImageView;
    8.  
    9. public class MainActivity extends Activity {
    10.  
    11. private boolean mIsCheck;
    12. private static final int[] STATE_CHECKED = new int[]{
    13. android.R.attr.state_checked};
    14. private static final int[] STATE_UNCHECKED = new int[]{};
    15. private ImageView mImageView;
    16. private Drawable mDrawable;
    17.  
    18. @Override
    19. protected void onCreate(Bundle savedInstanceState) {
    20. super.onCreate(savedInstanceState);
    21. setContentView(R.layout.activity_main);
    22. mImageView = (ImageView) findViewById(R.id.image);
    23. mDrawable = getResources().getDrawable(
    24. R.drawable.fab_anim);
    25. mImageView.setImageDrawable(mDrawable);
    26. }
    27.  
    28. public void anim(View view) {
    29. if (mIsCheck) {
    30. mImageView.setImageState(STATE_UNCHECKED, true);
    31. mIsCheck = false;
    32. } else {
    33. mImageView.setImageState(STATE_CHECKED, true);
    34. mIsCheck = true;
    35. }
    36. }
    37. }

    程序运行效果如图12.27所示,初始状态时,按钮显示“+”。而当点击时,按钮显示“√”,同时具有一个切换效果,而不是直接从“+”变为“√”,如图12.28所示。

    12.8 Material Design动画效果 - 图9 12.8 Material Design动画效果 - 图10
    图12.27 初始状态 图12.28 点击状态