写在前面的几句话
<p>
上一篇对Material Design 有简单的认识与了解了,我相信大家应该对Material Desgin有一定的认知了把,同时我相信绝大部分的童鞋应该对于其中的动画很感兴趣,那么这一篇就介绍下Material Design的动画实现,由于这些动画效果是在5.0上才提供Api的,那么如何在5.0以下的系统实现相似甚至相同的效果呢?那么本篇文章就对這方面进行详细的介绍,由于内容较多,那么我会分为上下两个文章进行说明。
分类
<p>
根据动画的类别和提供的类和属性等,可以将 Material Design Animation分为 6 类
Touch Feedback (触摸反馈)
Reveal Effect (揭露效果)
Curved Motion (曲线运动)
View State Changes (视图状态改变)
Animate View Drawables (可绘矢量动画)
Activity Transitions ( Activity 切换效果 )
接下来就对这6类动画进行说明,及在5.0系统下如何实现相似效果
<p>
一.Touch Feedback (触摸反馈)
<p>
触摸反馈应该很好理解,平时开发中,其实也会对触控有不同的反馈,比如变灰呀等等,但是5.0的触控反馈其实是加入了涟漪(波纹)效应,这也是与之前的触控反馈有不同的地方
按钮的默认触摸反馈动画是使用了新的RippleDrawable类,它会是波纹效果在不同状态间变换。
大多数情况下,我们可以使用这个功能通过在xml文件中定义背景:
android:attr/selectableItemBackground 有界限的波纹
android:attr/selectableItemBackgroundBorderless 可以超出视图区域的波纹 (21新添加的api)
<Button android:layout_width="100dp"
android:layout_height="50dp"
android:background="?android:attr/selectableItemBackground"
android:text="有界"
android:textColor="@android:color/white"
android:colorControlHighlight="@android:color/holo_red_dark"
android:layout_marginTop="20dp" />
<Button android:layout_width="100dp"
android:layout_height="50dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:textColor="@android:color/white"
android:text="无界"
android:layout_marginTop="20dp" />
你可以给RippleDrawable对象分配一个颜色。使用主题的android:colorControlHighlight
属性可以改变默认的触摸反馈颜色。
另外,也可以自定义按钮的效果,这里使用ripple元素定义RippleDrawable作为一个xml资源
ripple_button.xml
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="@android:color/holo_blue_light" android:radius="20dp">
<item android:drawable="@android:color/holo_green_dark" />
<!--<item>-->
<!--<shape-->
<!--android:shape="oval">-->
<!--<solid android:color="?android:colorAccent" />-->
<!--</shape>-->
<!--</item>-->
<!--<item>-->
<!--<shape android:shape="rectangle">-->
<!--<solid android:color="#FFFFFF" />-->
<!--<corners android:radius="4dp" />-->
<!--</shape>-->
<!--</item>-->
<!--<item android:drawable="@drawable/ic_launcher" />-->
</ripple>
xml定义了需要定义color,这个color代表按下后的涟漪效果的颜色,内部的内容则和Style样式一致
<Button android:layout_width="100dp" android:layout_height="50dp"
android:background="@drawable/ripple_button"
android:textColor="@android:color/white"
android:text="自定义1"
android:layout_marginTop="20dp"
/>
图1 5.0系统的触摸反馈
那么如何在低版本下实现这种涟漪的效果呢?
自然要去自定义控件来实现了
主要的代码则是这部分
//绘制按下后的整个背景
canvas.drawRect(pointX, pointY, pointX + viewWidth, pointY + viewHeight, bottomPaint);
canvas.save();
//绘制扩散圆形背景
canvas.clipRect(pointX, pointY, pointX + viewWidth, pointY + viewHeight);
canvas.drawCircle(eventX, eventY, shaderRadio, colorPaint);
canvas.restore();
这里则是绘制涟漪的代码,自定义控件又分为两个方向了,第一种是自定义单个控件,这个控件有这种点击的效果,另外一种则是定义一个layout,layout内部的控件点击都具有这种涟漪效果
图2 5.0以下实现触摸反馈的效果上面红色的区域为自定义的Button,下面的部分为自定义的layout,可以看到TextView也有与Button同样的效果
<p>
二.Reveal Effect (揭露效果)
<p>
揭露动画为用户提供视觉上的持续性挡显示或者隐藏一组界面元素
Android 5.0 引入了 ViewAnimationUtils.createCircularReveal()接口来提供动画效果来揭露或者隐藏一个视图
Animator animator;
if (isFirst){
animator = ViewAnimationUtils.createCircularReveal(
v,
cx,
cy,
v.getWidth(),
0
);
}else {
animator = ViewAnimationUtils.createCircularReveal(
v,
cx,
cy,
0,
v.getWidth()
);
}
animator.setInterpolator(new DecelerateInterpolator());
animator.setDuration(500);
图3 5.0的揭露效果
<p>
三.Curved Motion (曲线运动)
<p>
Material Design中,动画依赖时间插值和空间移动模式曲线。在android5.0(api 21)和更高版本,你可以为动画自定义时间曲线和移动曲线。
PathInterpolator: 以三次 bezier 曲线中间 2 个控制点的坐标来定义动画的速率快慢
PathInterpolator类是一个新的基于贝塞尔曲线或Path对象的插值器。这个插值器在1*1的正方形上定义了曲线运动,以(0,0)和(1,1)点作为锚点,根据够照参数控制点。你也可以使用xml文件的定义一个路径插值器,如:
<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
android:controlX1="0.4"
android:controlY1="0"
android:controlX2="1"
android:controlY2="1"/>
Material Design设计规范中,系统提供了三个基本曲线的xml资源:
- @interpolator/fast_out_linear_in.xml
- @interpolator/fast_out_slow_in.xml
- @interpolator/linear_out_slow_in.xml
我们可以给Animator.setInterpolator()传一个PathInterpolator对象来设置。
ObjectAnimator.ofFloat(T target, Property<T, Float> xProperty, Property<T, Float> yProperty, Path path): 中间的 path参数定义位移动画的路径。
Path path = new Path();
path.lineTo(0,300);
path.cubicTo(100, 0, 300, 900, 500, 600);
PathInterpolator pathInterpolator = new PathInterpolator(0.8f, 0f, 1f, 1f);
final ObjectAnimator mAnimator = ObjectAnimator.ofFloat(v, View.X, View.Y, path);
mAnimator.setInterpolator(pathInterpolator);
mAnimator.setDuration(3000);
mAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
}
});
mAnimator.start();
图4 5.0的曲线运动
那么如何在低版本下实现这种曲线运动的效果呢?
我总结了下有两种方式,一种为PathMeasure,另一种为TypeEvaluator,PathMeasure在前面的Path学习笔记中有介绍,关于TypeEvaluator会在后面动画学习笔记中进行介绍
直接上代码,第一种为PathMeasure实现方式
mPath = new Path();
mPath.moveTo(0, 0);
mPath.lineTo(0, 300);
mPath.cubicTo(100, 0, 300, 900, 500, 600);
mPathMeasure = new PathMeasure(mPath, true);
mCurrentPosition = new float[2];
ValueAnimator valueAnimator = ValueAnimator.ofFloat((float)0, mPathMeasure.getLength() - (float)Math.sqrt((double)(500*500 + 600*600)) );
valueAnimator.setDuration(3000);
// 减速插值器
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (Float) animation.getAnimatedValue();
// 获取当前点坐标封装到mCurrentPosition
mPathMeasure.getPosTan(value, mCurrentPosition, null);
postInvalidate();
}
});
valueAnimator.start();
canvas.drawCircle(mCurrentPosition[0], mCurrentPosition[1], 10, mPaint);
图4 PathMeasure的曲线运动
第二种为是TypeEvaluator实现方式
public class BezierEvaluator implements TypeEvaluator<PointF>{
//途径的两个点
private PointF pointF1;
private PointF pointF2;
public BezierEvaluator(PointF pointF1, PointF pointF2) {
this.pointF1 = pointF1;
this.pointF2 = pointF2;
}
@Override
public PointF evaluate(float time, PointF startValue, PointF endValue) {
float timeLeft = 1.0f - time;
PointF point = new PointF();
PointF point0 = (PointF)startValue;//起点
PointF point3 = (PointF)endValue;//终点
//代入公式
point.x = timeLeft * timeLeft * timeLeft * (point0.x)
+ 3 * timeLeft * timeLeft * time * (pointF1.x)
+ 3 * timeLeft * time * time * (pointF2.x)
+ time * time * time * (point3.x);
point.y = timeLeft * timeLeft * timeLeft * (point0.y)
+ 3 * timeLeft * timeLeft * time * (pointF1.y)
+ 3 * timeLeft * time * time * (pointF2.y)
+ time * time * time * (point3.y);
return point;
}
}
ObjectAnimator animator1 = ObjectAnimator.ofFloat(button,View.TRANSLATION_Y,0,300);
animator1.setDuration(600);
BezierEvaluator evaluator = new BezierEvaluator(new PointF(100,0),new PointF(300,900));
ValueAnimator animator = ValueAnimator.ofObject(evaluator,new PointF(0,300),new PointF(500,600));//随机
animator.addUpdateListener(new BezierListenr(button));
animator.setDuration(2400);
AnimatorSet allSet = new AnimatorSet();
allSet.play(animator1).before(animator);
allSet.start();
private class BezierListenr implements ValueAnimator.AnimatorUpdateListener{
private View target;
public BezierListenr(View target) {
this.target = target;
}
@Override
public void onAnimationUpdate(ValueAnimator animation) {
PointF pointF = (PointF) animation.getAnimatedValue();
target.setX(pointF.x);
target.setY(pointF.y);
}
}
图5 TypeEvaluator的曲线运动
四.View State Changes (视图状态改变)
<p>
Android 5.0提供了StateListAnimator类,可以用来定义动画集
1.定义一个XML资源的StateListAnimator
anim_statelistanimater
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<set>
<objectAnimator android:propertyName="translationZ"
android:duration="@android:integer/config_shortAnimTime"
android:valueTo="10"
android:valueType="floatType"/>
<objectAnimator android:propertyName="rotationX"
android:duration="@android:integer/config_shortAnimTime"
android:valueTo="360"
android:valueType="floatType"/>
</set>
</item>
<item android:state_pressed="false">
<set>
<objectAnimator android:propertyName="translationZ"
android:duration="10000"
android:valueTo="0"
android:valueType="floatType"/>
<objectAnimator android:propertyName="rotationX"
android:duration="@android:integer/config_shortAnimTime"
android:valueTo="0"
android:valueType="floatType"/>
</set>
</item>
</selector>
2.配置,配置分为两种一种为动态配置一种为静态配置
(1)静态配置
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="button2"
android:stateListAnimator="@anim/anim_statelistanimator"
/>
(2)动态配置
StateListAnimator stateLAnim = AnimatorInflater.loadStateListAnimator(this, R.anim.anim_statelistanimator);
button3.setStateListAnimator(stateLAnim);
图6 5.0的视图状态改变效果
那么如何在低版本下实现这种视图状态改变的效果呢?
很简单,其实XML资源的StateListAnimator就是一些Animator的集合
所以直接对需要动画的对象设置Animator就可以了
anim_statechange.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="together" >
<objectAnimator android:propertyName="translationZ"
android:duration="@android:integer/config_shortAnimTime"
android:valueTo="10"
android:valueType="floatType"/>
<objectAnimator android:propertyName="rotationX"
android:duration="@android:integer/config_shortAnimTime"
android:valueTo="360"
android:valueType="floatType"/>
</set>
anim = AnimatorInflater.loadAnimator(this, R.anim.anim_state);
anim.setTarget(view);
anim.start();
图7 5.0以下视图状态改变效果
写在后面的几句
<p>
至此我们分析了Touch Feedback (触摸反馈),Reveal Effect (揭露效果),Curved Motion (曲线运动),View State Changes (视图状态改变)动画效果在5.0及5.0以下的实现效果,那么后面的两个Animate View Drawables (可绘矢量动画),Activity Transitions ( Activity 切换效果 )会在Material Design(三)动画实现下中进行分析。。
网友评论