【Transition】Android炫酷的Activity切换效果,共享元素

如果本文帮助到你,本人不胜荣幸,如果浪费了你的时间,本人深感抱歉。
希望用最简单的大白话来帮助那些像我一样的人。如果有什么错误,请一定指出,以免误导大家、也误导我。
本文来自:http://www.jianshu.com/users/320f9e8f7fc9/latest_articles
感谢您的关注。

本文原项目地址为:
https://github.com/lgvalle/Material-Animations
在文章最后,有我自己对着这个项目手敲的一份。
代码基本一模一样,只有略微的修改,加了一些注释,以及将其中大多数英文翻译成了中文。


此篇 API 均为 Android 5.0(API 级别 21) 以上才可支持。
此demo一共分为四部分:

1.1 普通过渡 Transition;
1.2 Shared Elements Transition 共享元素;
2.0 TransitionManager 控制动画;
3.0 ViewAnimationUtils 显示或隐藏效果。

过渡效果 Transition

Material Design 为应用中的切换页面时,提供了非常优雅的视觉切换效果。
您可为进入、退出转换、页面之间的共享元素转换设置特定的动画。

1. Transition 动画都包含哪些?

Android 5.0(API 级别 21)支持的进入与退出转换有三个:

Explode Slide Fade
从中心移入或移出 从边缘移入或移出 调整透明度产生渐变

一会看到使用场景的时候,就会发现上面的三张图,每张图都经历了:(此处可以一会再回过头来看)

退出 -> 进入  -> 返回   -> 重新进入
Exit -> Enter -> Return -> Reenter

**第一个页面设置:**
android:windowExitTransition      启动新 Activity ,此页面退出的动画
android:windowReenterTransition   重新进入的动画。即第二次进入,可以和首次进入不一样。
**第二个页面设置:**
android:windowEnterTransition     首次进入显示的动画
android:windowReturnTransition    调用 finishAfterTransition() 退出时,此页面退出的动画

如此即可达到以上效果。

explode:从场景的中心移入或移出
slide:从场景的边缘移入或移出
fade:调整透明度产生渐变效果

这三个类都继承于 Transition ,所有有一些属性都是共同的。
常用属性如下:

// 设置动画的时间。类型:long
transition.setDuration();
// 设置修饰动画,定义动画的变化率,具体设置往下翻就看到了
transition.setInterpolator();
// 设置动画开始时间,延迟n毫秒播放。类型:long
transition.setStartDelay();
// 设置动画的运行路径
transition.setPathMotion();
// 改变动画 出现/消失 的模式。Visibility.MODE_IN:进入;Visibility.MODE_OUT:退出。
transition.setMode();

// 设置动画的监听事件
transition.addListener()

至于例子,在下一个给出。
喏,这不就是了。

2. 修饰动画,定义动画的变化率(Interpolator)

在 Java 代码中定义:

Explode transition = new Explode();
transition.setDuration(500);
transition.setInterpolator(new AccelerateInterpolator());

在 Xml 资源定义:

<explode
    android:duration="@integer/anim_duration_long"
    android:interpolator="@android:interpolator/bounce"
    />

可选类型:

AccelerateDecelerateInterpolator 在动画开始与结束的地方速率改变比较慢,在中间的时候加速

AccelerateInterpolator  在动画开始的地方速率改变比较慢,然后开始加速

AnticipateInterpolator 开始的时候向后然后向前甩

AnticipateOvershootInterpolator 开始的时候向后然后向前甩一定值后返回最后的值

BounceInterpolator   动画结束的时候弹起

CycleInterpolator 动画循环播放特定的次数,速率改变沿着正弦曲线

DecelerateInterpolator 在动画开始的地方快然后慢

LinearInterpolator   以常量速率改变

OvershootInterpolator    向前甩一定值后再回到原来位置
3. 设置 Transition 的时机

到底该什么时候,设置什么样的过渡呢?

以下为动画的设置场景:

首先打开页面A :
页面A -> Enter 首次进入

从 A 打开 B :
页面A -> Exit 退出
页面B -> Enter 首次进入

从 B 返回 A :
页面B -> Return 返回
页面A -> Reenter 重新进入

可设置的方法如下:

android:windowContentTransitions                允许使用transitions

android:windowAllowEnterTransitionOverlap       是否覆盖执行,其实可以理解成前后两个页面是同步执行还是顺序执行

android:windowAllowReturnTransitionOverlap      与上面相同。即上一个设置了退出动画,这个设置了进入动画,两者是否同时执行。

android:windowContentTransitionManager          引用TransitionManager XML资源,定义不同窗口内容之间的所需转换。



android:windowEnterTransition                   首次进入显示的动画

android:windowExitTransition                    启动新 Activity ,此页面退出的动画

android:windowReenterTransition                 重新进入的动画。即第二次进入,可以和首次进入不一样。

android:windowReturnTransition                  调用 finishAfterTransition() 退出时,此页面退出的动画



android:windowSharedElementsUseOverlay          指示共享元素在转换期间是否应使用叠加层。

android:windowSharedElementEnterTransition      首次进入显示的动画

android:windowSharedElementExitTransition       启动新 Activity ,此页面退出的动画

android:windowSharedElementReenterTransition    重新进入的动画。即第二次进入,可以和首次进入不一样。

android:windowSharedElementReturnTransition     调用 finishAfterTransition() 退出时,此页面退出的动画

以上为 style 中设置属性,在代码中设置为:

 getWindow().setEnterTransition(visibility);
 // 其余的都是类似
4. 跳转页面

至此,用以上知识基本可以设置出绝大多数的过渡效果。
然后,跳转页面跟普通的跳转也有点不一样。

跳转页面:

protected void transitionTo(Intent i) {
    ActivityOptionsCompat transitionActivityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation(this);
    startActivity(i, transitionActivityOptions.toBundle());
}

退出页面:

private void closeActivity(){
    // 如果定义了 return transition ,将使用 定义的动画过渡
    Visibility returnTransition = buildReturnTransition();
    getWindow().setReturnTransition(returnTransition);

    // 如果没有 return transition 被定义,将使用 反进入 的动画
    finishAfterTransition();
}

注意:退出时,一定要调用:finishAfterTransition();

通过以上设置,就能够完成一个基本的过渡效果了。


Shared Elements Transition 共享元素

效果如图:

Shared Elements Transition

共享元素的各种设置 与 普通Transition 差不多。
来说说不同的地方。

携带需要共享的 View 进行跳转

也就是在跳转的参数中,增加了要共享的 View控件。

  1. 先在 layout 布局里面给需要共享的元素加上如下字段,这样两个 View 就能互通了:
android:transitionName="image"
  1. 然后开始设置跳转页面,代码如下:
protected void transitionTo(Intent i) {
    // 这里携带了两个 View
    ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this,
                                                    Pair.create(view1, "agreedName1"),
                                                    Pair.create(view2, "agreedName2"));
    startActivity(i, options.toBundle());
}

增加的参数的介绍:

Pair.create(
   View view,      // 本页面要共享的 View
   String resId    // 下一个页面的 View 的 id,注意是 id 的字符串
)
页面切换时,动画的效果设置

Android 5.0(API 级别 21)支持转换效果如下:

changeBounds - 改变目标视图的布局边界
changeClipBounds - 裁剪目标视图边界
changeTransform - 改变目标视图的缩放比例和旋转角度
changeImageTransform - 改变目标图片的大小和缩放比例

设置代码:

Slide slide = new Slide();
slide.setDuration(500);

ChangeBounds changeBounds = new ChangeBounds();
changeBounds.setDuration(500);

getWindow().setEnterTransition(slide);
getWindow().setSharedElementEnterTransition(changeBounds);

Shared Elements Transition 就是 特殊的 Transition 用法,都是一样的。


TransitionManager 控制动画

这个框架可以让一些复杂的动画特别简单的被实现。

TransitionManager

简单的说明一下步骤:

  1. 定义需要切换 layout xml页面;
  2. 调用 Scene.getSceneForLayout() 保存每个Layout;
  3. 调用 TransitionManager.go(scene1, new ChangeBounds()) 切换。

相当于定义了不同的 xml 布局,然后通过简单的调用,就完成了较为复杂的动画。

以下为代码片段:

private void setupLayout() {
    scene0 = Scene.getSceneForLayout(binding.sceneRoot, R.layout.activity_animations_scene0, this);
    scene1 = Scene.getSceneForLayout(binding.sceneRoot, R.layout.activity_animations_scene1, this);
    scene2 = Scene.getSceneForLayout(binding.sceneRoot, R.layout.activity_animations_scene2, this);
    scene3 = Scene.getSceneForLayout(binding.sceneRoot, R.layout.activity_animations_scene3, this);
    scene4 = Scene.getSceneForLayout(binding.sceneRoot, R.layout.activity_animations_scene4, this);
    binding.sample3Button1.setOnClickListener(this);
    binding.sample3Button2.setOnClickListener(this);
    binding.sample3Button3.setOnClickListener(this);
    binding.sample3Button4.setOnClickListener(this);
}

@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.sample3_button1:
            TransitionManager.go(scene1, new ChangeBounds());
            break;
        case R.id.sample3_button2:
            TransitionManager.go(scene2, TransitionInflater.from(this).inflateTransition(R.transition.slide_and_changebounds));
            break;
        case R.id.sample3_button3:
            TransitionManager.go(scene3,TransitionInflater.from(this).inflateTransition(R.transition.slide_and_changebounds_sequential));
            break;
        case R.id.sample3_button4:
            TransitionManager.go(scene3,TransitionInflater.from(this).inflateTransition(R.transition.slide_and_changebounds_sequential_with_interpolators));
            break;
    }
}

只需要定义布局,调用调转,
就实现了,点击四个 Button ,分别切换四个布局。
TransitionManager.go() 中可以设置各种动画。


CircularReveal 显示或隐藏 的效果

ViewAnimationUtils.createCircularReveal()
当您显示或隐藏一组 UI 元素时,Circular Reveal 可为用户提供视觉连续性

CircularReveal

参数说明:

Animator createCircularReveal (View view, // 将要变化的 View
            int centerX,                  // 动画圆的中心的x坐标
            int centerY,                  // 动画圆的中心的y坐标
            float startRadius,            // 动画圆的起始半径
            float endRadius               // 动画圆的结束半径
)

显示 View :

private void animShow() {
    View myView = findViewById(R.id.my_view);
    // 从 View 的中心开始
    int cx = (myView.getLeft() + myView.getRight()) / 2;
    int cy = (myView.getTop() + myView.getBottom()) / 2;
    int finalRadius = Math.max(myView.getWidth(), myView.getHeight());

    //为此视图创建动画设计(起始半径为零)
    Animator anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0, finalRadius);
    // 使视图可见并启动动画
    myView.setVisibility(View.VISIBLE);
    anim.start();
}

隐藏 View :

private void animHide() {
    final View myView = findViewById(R.id.my_view);
    int cx = (myView.getLeft() + myView.getRight()) / 2;
    int cy = (myView.getTop() + myView.getBottom()) / 2;

    int initialRadius = myView.getWidth();

    // 半径 从 viewWidth -> 0
    Animator anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, initialRadius, 0);

    anim.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationEnd(Animator animation) {
            super.onAnimationEnd(animation);
            myView.setVisibility(View.INVISIBLE);
        }
    });
    anim.start();
}

因为,这些炫酷的效果都只支持 API 23 以上,所以在我们常用的 APP 中都还不常见。但是效果真的很不错。

值得大家研究一下。
此文是我的一个总结。

项目地址:
https://github.com/Wing-Li/Material-Animations-CN

这个项目是我对着原项目手敲的,
基本一模一样,只是加了一些注释,以及将其中英文翻译成了中文。
大家可以参考参考。

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,830评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,066评论 4 62
  • 内容抽屉菜单ListViewWebViewSwitchButton按钮点赞按钮进度条TabLayout图标下拉刷新...
    皇小弟阅读 46,733评论 22 665
  • 丫头回来,想着看能不能多做个菜。围着围裙出门,想看看刚才路口卖菜的摊点还在不在。20米远的路,我手抄口袋自顾自朝前...
    知画秋婵阅读 315评论 3 5
  • 盛开的花朵是春雨的答案 羞涩的青果是夏日的答案 秋天的答案在稻穗尖上 冬天的答案融化在雪地里 没有谁能束缚住谁 爱...
    一言尔阅读 286评论 5 0