[关闭]
@zyl06 2017-02-11T15:46:44.000000Z 字数 10492 阅读 1394

Android Material Design Animation (一)

Android


1 动画的规范

Material Design 是 Google 总结的一套具有创新性和符合科学的视觉规范,其中包含动画、排版、控件、资源、样式等,包含的内容比较全,感兴趣的同学可以查看官方文档 Material design

对于动画,Material Design 定义的一套规则,这里简单总结如下:

2 动画的实现

贴心的 Google 大神么不仅定义了这些规范,也提供相关的类和方法帮助我们实现他们的规范。根据动画的类别和提供的类和属性等,大概整理下,这里将 Material Design Animation 分为 6 类,分别是:

2.1 Touch Feedback (触摸反馈)

触摸反馈比较好理解,大家在做项目的时候,经常碰到视觉同学会要求我们做出来的按钮在点击的时候,修改外观,比如置灰显示。下面这段 xml 代码,就是解决这个问题的一种常规办法,相信 Android 小伙伴们都非常熟悉,当然不使用 xml 的话,也可以自定义在 Java 代码中自定义使用 ColorStateList,来达到相同的效果。不过这种常规实现的效果中规中矩,不算酷炫。

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <selector xmlns:android="http://schemas.android.com/apk/res/android">
  3. <item android:drawable="@mipmap/ic_btn_pressed" android:state_pressed="true"/>
  4. <item android:drawable="@mipmap/ic_btn"/>
  5. </selector>

为了实现上面规范中讲述的波纹效果,可以使用 RippleDrawable 类,简单的我们可以在 xml 文件中定义。

整体显示效果如下:

image

另外,因为 Touch Feedback 是 Android 5.0 上默认支持的,如果需要在低版本的 Android 应用中也要实现的话,那可以参见 RippleEffect

2.2 Reveal Effect (揭露效果)

Android 5.0 引入了 ViewAnimationUtils.createCircularReveal() 接口,能很方便的实现圆形缩放效果。

  1. private static void circularRevealBox(View view) {
  2. Rect rect = new Rect();
  3. view.getLocalVisibleRect(rect);
  4. int cx = rect.left + rect.width()/2;
  5. int cy = rect.top + rect.height()/2;
  6. // get the final radius for the clipping circle
  7. int finalRadius = view.getWidth();
  8. Animator anim = ViewAnimationUtils.createCircularReveal(view, cx, cy, 0, finalRadius);
  9. anim.setDuration(1000);
  10. anim.start();
  11. view.setVisibility(View.VISIBLE);
  12. }

效果如下:

image

2.3 Curved Motion (曲线运动)

2.3.1 使用 TypeEvaluator 实现

在 Android 5.0 之前,如果需要实现曲线位移运动,则需要自己写过程插值器 TypeEvaluator,代码如下:

  1. public class PathEvaluator implements TypeEvaluator<PathPoint> {
  2. @Override
  3. public PathPoint evaluate(float t, PathPoint startValue, PathPoint endValue) {
  4. float x, y;
  5. if (endValue.mOperation == PathPoint.CURVE) {
  6. float oneMinusT = 1 - t;
  7. x = oneMinusT * oneMinusT * oneMinusT * startValue.mX +
  8. 3 * oneMinusT * oneMinusT * t * endValue.mControl0X +
  9. 3 * oneMinusT * t * t * endValue.mControl1X +
  10. t * t * t * endValue.mX;
  11. y = oneMinusT * oneMinusT * oneMinusT * startValue.mY +
  12. 3 * oneMinusT * oneMinusT * t * endValue.mControl0Y +
  13. 3 * oneMinusT * t * t * endValue.mControl1Y +
  14. t * t * t * endValue.mY;
  15. } else if (endValue.mOperation == PathPoint.LINE) {
  16. x = startValue.mX + t * (endValue.mX - startValue.mX);
  17. y = startValue.mY + t * (endValue.mY - startValue.mY);
  18. } else {
  19. x = endValue.mX;
  20. y = endValue.mY;
  21. }
  22. return PathPoint.moveTo(x, y);
  23. }
  24. }

自定义的动画路径类 AnimatorPath

  1. public class AnimatorPath {
  2. // The points in the path
  3. ArrayList<PathPoint> mPoints = new ArrayList<PathPoint>();
  4. /**
  5. * Move from the current path point to the new one
  6. * specified by x and y. This will create a discontinuity if this point is
  7. * neither the first point in the path nor the same as the previous point
  8. * in the path.
  9. */
  10. public void moveTo(float x, float y) {
  11. mPoints.add(PathPoint.moveTo(x, y));
  12. }
  13. /**
  14. * Create a straight line from the current path point to the new one
  15. * specified by x and y.
  16. */
  17. public void lineTo(float x, float y) {
  18. mPoints.add(PathPoint.lineTo(x, y));
  19. }
  20. /**
  21. * Create a cubic Bezier curve from the current path point to the new one
  22. * specified by x and y. The curve uses the current path location as the first anchor
  23. * point, the control points (c0X, c0Y) and (c1X, c1Y), and (x, y) as the end anchor point.
  24. */
  25. public void curveTo(float c0X, float c0Y, float c1X, float c1Y, float x, float y) {
  26. mPoints.add(PathPoint.curveTo(c0X, c0Y, c1X, c1Y, x, y));
  27. }
  28. /**
  29. * Returns a Collection of PathPoint objects that describe all points in the path.
  30. */
  31. public Collection<PathPoint> getPoints() {
  32. return mPoints;
  33. }
  34. }

之后曲线动画设置和启动代码如下

  1. // Set up the path we're animating along
  2. AnimatorPath path = new AnimatorPath();
  3. path.moveTo(0, 0);
  4. path.lineTo(0, 300);
  5. path.curveTo(100, 0, 300, 900, 400, 500);
  6. // Set up the animation
  7. final ObjectAnimator anim = ObjectAnimator.ofObject(this, "buttonLoc",
  8. new PathEvaluator(), path.getPoints().toArray());
  9. anim.setDuration(3000);
  10. anim.addListener(new Animator.AnimatorListener() {
  11. @Override
  12. public void onAnimationStart(Animator animation) {
  13. mMaterialButton.setVisibility(View.INVISIBLE);
  14. }
  15. @Override
  16. public void onAnimationEnd(Animator animation) {
  17. mMaterialButton.setVisibility(View.VISIBLE);
  18. }
  19. @Override
  20. public void onAnimationCancel(Animator animation) {
  21. }
  22. @Override
  23. public void onAnimationRepeat(Animator animation) {
  24. }
  25. });

效果如下:

image

2.3.2 使用 PathInterpolator 和 ObjectAnimator.ofFloat 接口实现

在 Android 5.0 提供了

  1. PathInterpolator: 以三次 bezier 曲线中间 2 个控制点的坐标来定义动画的速率快慢

  2. ObjectAnimator.ofFloat(T target, Property<T, Float> xProperty, Property<T, Float> yProperty, Path path): 中间的 path 参数定义位移动画的路径。

  1. Path path = new Path();
  2. //path.moveTo(0, 100);
  3. path.lineTo(0, 400);
  4. path.cubicTo(100, 0, 300, 900, 500, 600);
  5. PathInterpolator pathInterpolator = new PathInterpolator(0.8f, 0f, 1f, 1f);
  6. final ObjectAnimator mAnimator = ObjectAnimator.ofFloat(mMaterialButton, View.X, View.Y, path);
  7. mAnimator.setInterpolator(pathInterpolator);
  8. mAnimator.setDuration(3000);

效果如下:

image

2.4 View State Changes (视图状态改变)

在 Android 5.0 新提供了 StateListAnimatorAnimatedStateListDrawable 类用于实现视图状态改变动画

  1. StateListAnimator

    res/anim/anim_view_state_changes.xml

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <!-- animate the translationZ property of a view when pressed -->
    3. <selector xmlns:android="http://schemas.android.com/apk/res/android">
    4. <item android:state_pressed="true">
    5. <set>
    6. <objectAnimator android:propertyName="translationZ"
    7. android:duration="@android:integer/config_shortAnimTime"
    8. android:valueTo="4dp"
    9. android:valueType="floatType"/>
    10. </set>
    11. </item>
    12. <item
    13. android:state_pressed="false">
    14. <set>
    15. <objectAnimator android:propertyName="translationZ"
    16. android:duration="100"
    17. android:valueTo="0"
    18. android:valueType="floatType"/>
    19. </set>
    20. </item>
    21. </selector>

    说明 :定义了一个按下时,控件 Z 值变大的动画;松开时,控件 Z 值变小的动画。其中 android 中 Z 值的改变是通过控件的阴影大小来体现

    具体应用:

    • xml 中定义
    1. <TextView
    2. android:text="animate_view_state_changes"
    3. android:layout_width="100dp"
    4. android:layout_height="100dp"
    5. android:stateListAnimator="@anim/anim_view_state_changes"
    • java 中定义
    1. TextView textView = (TextView)findViewById(R.id.animate_view_state_changes);
    2. StateListAnimator stateLAnim = AnimatorInflater.loadStateListAnimator(this,
    3. R.anim.anim_view_state_changes);
    4. textView.setStateListAnimator(stateLAnim);

    效果图如下:

    image

  2. AnimatedStateListDrawable

    res/drawable/my_anim_state_drawable.xml

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
    3. <!-- provide a different drawable for each state-->
    4. <item android:id="@+id/pressed"
    5. android:drawable="@drawable/bg_loadmore5"
    6. android:state_pressed="true"/>
    7. <item android:id="@+id/focused"
    8. android:drawable="@drawable/bg_loadmore5"
    9. android:state_focused="true"/>
    10. <item android:id="@+id/normal"
    11. android:drawable="@drawable/bg_loadmore0"/>
    12. <!-- specify a transition -->
    13. <transition android:fromId="@+id/normal" android:toId="@+id/pressed">
    14. <animation-list>
    15. <item android:duration="15" android:drawable="@drawable/bg_loadmore0"/>
    16. <item android:duration="15" android:drawable="@drawable/bg_loadmore1"/>
    17. <item android:duration="15" android:drawable="@drawable/bg_loadmore2"/>
    18. <item android:duration="15" android:drawable="@drawable/bg_loadmore3"/>
    19. <item android:duration="15" android:drawable="@drawable/bg_loadmore4"/>
    20. <item android:duration="15" android:drawable="@drawable/bg_loadmore5"/>
    21. </animation-list>
    22. </transition>
    23. </animated-selector>

    说明 :当处于press状态的时候,animation-list 正着走一遍,drawable使用最后一个;当处于press状态的时候,animation-list反着走一遍,drawable使用第一个

    具体应用:

    1. <TextView
    2. android:text="my_anim_state_drawable"
    3. android:layout_width="250dp"
    4. android:layout_height="100dp"
    5. android:background="@drawable/my_anim_state_drawable" />

    效果图如下:

    image

2.5 Animate View Drawables (可绘矢量动画)

通常在三种xml定义的矢量图

  1. 静态矢量图,使用 <vector> 元素的矢量图,在 res/drawable/

    矢量图可以定义的属性元素有和,定义了一个的集合,或者子,定义绘制的路径。

    Google 官方示例代码

    1. <vector xmlns:android="http://schemas.android.com/apk/res/android"
    2. android:height="64dp"
    3. android:width="64dp"
    4. android:viewportHeight="600"
    5. android:viewportWidth="600" >
    6. <group
    7. android:name="rotationGroup"
    8. android:pivotX="300.0"
    9. android:pivotY="300.0"
    10. android:rotation="45.0" >
    11. <path
    12. android:name="v"
    13. android:fillColor="#000000"
    14. android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
    15. </group>
    16. </vector>
  2. 动态矢量图,使用 <objectAnimator> 元素,在 res/animator

    1. <objectAnimator
    2. xmlns:android="http://schemas.android.com/apk/res/android"
    3. android:duration="3000"
    4. android:propertyName="pathData"
    5. android:valueFrom="M300,70 l 0,-70 70,70 0,0 -70,70z"
    6. android:valueTo="M300,70 l 0,-70 70,0 0,140 -70,0 z"
    7. android:valueType="pathType" />

    其中,valueFromvalueTo 是矢量动画的关键,分别指定了矢量图的起始路径。

    这里的矢量指令是 SVG 标准指令,规则如下:

    • 代码中的 M 表示 MoveTo; l 表示 LineTo;z` 表示收尾闭合

    • 矢量图 path 从一个图形到另一个,fromto 的路径必须一致:相同数量的命令和相同数量的每个命令的参数

    • 全部的指令使用一个字母表示,如 Ml

    • 指令中的逗号、空格符可以被省略,如 M 100 100 L 200 20M100 100L200 200

    • 连续使用的相同指令可以被省略,如M 100 200 L 200 100 L -100 -200 → M 100 200 L 200 100 -100 -200

    • 大写指令代码绝对坐标,小写指令代码相对坐标

    • Unicode U+0046 FULL STOP (“.”) 是唯一被允许的小数点。如数字 13,000.56 是不合法的

    • 进一步的详细细节请参见 SVG Paths

  3. 一个或多个 object animator,使用 <animated-vector> 元素,在 res/drawable/

    示例代码

    res/drawable/face.xml 如下:

    1. <vector
    2. xmlns:android="http://schemas.android.com/apk/res/android"
    3. android:height="200dp"
    4. android:width="200dp"
    5. android:viewportHeight="100"
    6. android:viewportWidth="100" >
    7. <path
    8. android:fillColor="@color/yellow"
    9. android:pathData="@string/path_circle"/>
    10. <path
    11. android:fillColor="@android:color/black"
    12. android:pathData="@string/path_face_left_eye"/>
    13. <path
    14. android:fillColor="@android:color/black"
    15. android:pathData="@string/path_face_right_eye"/>
    16. <path
    17. android:name="mouth"
    18. android:strokeColor="@android:color/black"
    19. android:strokeWidth="@integer/stroke_width"
    20. android:strokeLineCap="round"
    21. android:pathData="@string/path_face_mouth_sad"/>
    22. </vector>

    res/drawable/avd.xml 如下:

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <animated-vector
    3. xmlns:android="http://schemas.android.com/apk/res/android"
    4. android:drawable="@drawable/face" >
    5. <target
    6. android:name="mouth"
    7. android:animation="@anim/smile" />
    8. </animated-vector>

    说明<target> 标签里面的 name 标签指定 face.xml 中必须一个 name 相吻合

    具体应用

    1. <ImageView
    2. android:id="@+id/image"
    3. android:layout_width="wrap_content"
    4. android:layout_height="wrap_content"
    5. android:src="@drawable/avd" />

    效果图如下:

    image

2.6 Activity Transitions ( Activity 切换效果 )

Andorid Material Design Animation(二)

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注