Material Design(二)动画实现上

写在前面的几句话

<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(三)动画实现下中进行分析。。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,037评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,089评论 4 62
  • 内容抽屉菜单ListViewWebViewSwitchButton按钮点赞按钮进度条TabLayout图标下拉刷新...
    皇小弟阅读 46,750评论 22 665
  • 清晨在菊次郎的春天中缓缓苏醒,拖着昨日未消的倦气。 客厅里坐着的是五点半便爬起的老父亲,水烟筒咕嘟咕嘟的叫...
    丫丫丫吖吖阅读 224评论 0 0
  • 每个下雨的日子 我都想你 不只因为那是你的名字 还有我和你一起淋过的雨 每个寂寞的晚上 我都想你 不只因为寂寞淹没...
    熊猫二先生阅读 97评论 2 3