四、Content Transition实现非共享元素转场

1. 梗概

!!! 拆分一个页面的不同元素view,实现不同的animation...但是元素是 非共享的 !!!

content transition决定了非共享view元素在activity和fragment切换期间是如何进入或者退出场景的。根据google最新的Material Design设计语言,content transition让我们毫不费力的去协调Activity/Fragment切换过程中view的进入和退出,让这个过程更流畅。在5.0之后content transition可以通过调用Window和Fragment的如下代码来设置:

setExitTransition()     //当A start B时,使A中的View退出场景的transition

setEnterTransition()   //当A start B时,使B中的View进入场景的transition

setReturnTransition()  //当B 返回 A时,使B中的View退出场景的transition

setReenterTransition() //当B 返回 A时,使A中的View进入场景的transition

//也可以直接在xml设置,通常都这么做
        <item name="android:windowEnterTransition"></item>
        <item name="android:windowExitTransition"></item>
        <item name="android:windowReenterTransition"></item>
        <item name="android:windowReturnTransition"></item>

以下图为例,演示了google play Games app如何通过content transition实现activity之间的平滑切换。当第二个activity开始的时候,enter transition让用户的头像从底部边缘慢慢滑入。而在activity退出的时候,屏幕被分成两半,各自消失在上下边缘。


Content Transition 例子

2. 那么怎么使用Content Transition呢?

2.1 xml实现

  1. 定义 界面指定元素的转场动画
    三、定义 界面指定元素 或界面间共享元素 的转场动画基础

  2. 在Manifest.xml为相应的Activity设置相对于的style

    <style name="BaseAnimationAppTheme" parent="android:Theme.Material">
        <!-- 1. 开启过渡效果-->
        <item name="android:windowContentTransitions">true</item>

        <!-- 2. 指定 界面元素 进入/退出的动画效果,可以不全部实现(当然可以在代码中设置,前文已提)-->
        <item name="android:windowEnterTransition">@anim/search_enter</item>
        <item name="android:windowExitTransition">...</item>
        <item name="android:windowReenterTransition">...</item>
        <item name="android:windowReturnTransition">...</item>

    
        /**还可以设置是否同步执行还是顺序执行
        *默认情况下,material主题的应用中enter/return的content transition
        *会在exit/reenter的content transitions结束之前开始播放(只是稍微早于)
        */
        <!--A退出的动画和B进入的动画同步进行,代码中setWindowAllowEnterTransitionOverlap()实现-->
        <item name="android:windowAllowEnterTransitionOverlap">true</item>
        <!--B返回的动画和A重新进入的动画同步进行,代码中setWindowAllowReturnTransitionOverlap()实现-->
        <item name="android:windowAllowReturnTransitionOverlap">true</item>

    </style>
  1. 启动Activity
ActivityOptionsCompat optionsCompat=ActivityOptionsCompat.makeSceneTransitionAnimation(this);
ActivityCompat.startActivity(this, intent, optionsCompat.toBundle());

4.退出时调用finishAfterTransition()

注意
1、Material主题默认会将exit的transition设置成null,enter的transition设置成Fade 。
2、如果reenter 或者 return transition没有明确设置,则将用exit 和enter的共享元素transition替代

2.2 Java代码实现(不推荐)

1、activity的style中开启内容过渡效果,并设置相应的theme

 <item name="android:windowContentTransitions">true</item>

    <!--A退出的动画和B进入的动画同步进行-->
    <item name="android:windowAllowEnterTransitionOverlap">true</item>
    <!--B返回的动画和A重新进入的动画同步进行-->
    <item name="android:windowAllowReturnTransitionOverlap">true</item

2.启动activity B

ActivityOptionsCompat optionsCompat=ActivityOptionsCompat.makeSceneTransitionAnimation(this);
ActivityCompat.startActivity(this, intent, optionsCompat.toBundle());

3、在B中使用内容变换

Slide slide=new Slide(Gravity.BOTTOM);
        slide.setDuration(500);
        //内容变换,不包括底部导航栏和状态栏
        slide.excludeTarget(android.R.id.navigationBarBackground, true);
        slide.excludeTarget(android.R.id.statusBarBackground, true);
        slide.excludeTarget(R.id.appBarLayout, true);
        getWindow().setEnterTransition(slide);
        getWindow().setReturnTransition(slide);


//也可以在xml文件设置transition,使用TransitionInflater得到Transition,如下:
Transition slide=TransitionInflater.from(this).inflateTransition(R.transition.slide_anim);
slide.setDuration(500);
slide.excludeTarget(R.id.appBarLayout, true);
getWindow().setEnterTransition(slide);
getWindow().setReturnTransition(slide);

4.return时调用finishAfterTransition() :非强制的


3. 深入分析

3.1 Activity A 调用startActivity()

1.framework遍历A的View树,确定当A的exit transition运行时哪些view会退出场景(即哪些view是transitioning view)。

2.A的exit transition捕获A中transitioning view的开始状态。

3.framework将A中所有的transitioning view设置为INVISIBLE。

4.A的exit transition捕获到A中transitioning view的结束状态。

5.A的exit transition比较每个transitioning view的开始和结束状态,然后根据前后状态的区别创建一个Animator。Animator开始运行,同时transitioning view退出场景。

3.2 Activity B启动

1.framework遍历B的View树,确定当B的enter transition运行时哪些view会进入场景,transitioning view会被初始化为INVISIBLE。

2.B的enter transition捕获B中transitioning view的开始状态。

3.framework将B中所有的transitioning view设置为VISIBLE。

4.B的enter transition捕获到B中transitioning view的结束状态。

5.B的enter transition比较每个transitioning view的开始和结束状态,然后根据前后状态的区别创建一个Animator。Animator开始运行,同时transitioning view进入场景。

通过在每个transitioning view中来回切换INVISIBLE 和VISIBLE,framework确保content transition得到创建animation(期望的animation)所需的状态信息。

3.3 监听动画的变化过程

 //当然也可以监听 Return,Exit,Reenter时的动画
        getWindow().getEnterTransition().addListener(new Transition.TransitionListener() {
            @Override
            public void onTransitionStart(Transition transition) {
                
            }

            @Override
            public void onTransitionEnd(Transition transition) {

            }

            @Override
            public void onTransitionCancel(Transition transition) {

            }

            @Override
            public void onTransitionPause(Transition transition) {

            }

            @Override
            public void onTransitionResume(Transition transition) {

            }
        });

下面来看一个简单的实现,先看效果


GIFtransitionelements.gif
//AndroidManifest.xml
        <activity
            android:name=".ContentTransitionElementsActivity"
            android:label="ContentTransitionElements"
            android:theme="@style/AppTheme.ContentTransition" />

//style.xml
    <style name="AppTheme.ContentTransition">
        <item name="android:windowContentTransitions">true</item>
        <item name="android:windowActivityTransitions">true</item>
        <item name="android:windowEnterTransition">@transition/enter_content_transition</item>
        <item name="android:windowReturnTransition">@transition/return_content_transition</item>
    </style>

enter_content_transition.xml

<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">

    <targets >
        <target android:excludeId="@android:id/statusBarBackground"/>  <!--状态栏-->
        <target android:excludeId="@android:id/navigationBarBackground"/> <!--导航栏-->
    </targets>

<!--    <fade android:duration="500">
        <targets>
            <target android:targetId="@android:id/statusBarBackground"/>
        </targets>
    </fade>-->

    <slide android:slideEdge="left"  android:startDelay="500">
        <targets >
            <target android:targetId="@id/tv_show"/>
        </targets>
    </slide>

    <slide android:startDelay="700">
        <targets >
            <target android:targetId="@id/iv_left"/>
        </targets>
    </slide>

    <slide android:startDelay="900">
        <targets >
            <target android:targetId="@id/iv_right"/>
        </targets>
    </slide>

</transitionSet>

return_content_transition.xml

<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="800">

    <slide android:slideEdge="top">
        <targets>
            <target android:targetId="@id/relay_flag" />
            <!--<target  android:targetId="@id/fab"/>
            <target android:targetId="@id/icon_gg"/>
            <target android:targetId="@id/image_bg"/>-->

        </targets>
    </slide>

    <slide android:slideEdge="bottom">
        <targets>
            <target android:targetId="@id/bottom_container" />
            <target android:targetId="@id/tv_show"/>
            <target android:targetId="@id/iv_left"/>
            <target android:targetId="@id/iv_right"/>
        </targets>
    </slide>


        <fade>
            <targets >
                <target android:targetId="@android:id/statusBarBackground"/>
            </targets>
        </fade>
</transitionSet>
public class ContentTransitionElementsActivity extends AppCompatActivity {

    FloatingActionButton fab;
    View image_bg;
    ImageView icon_gg;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_content_transition);
        fab=findViewById(R.id.fab);
        image_bg=findViewById(R.id.image_bg);
        icon_gg=findViewById(R.id.icon_gg);

        getWindow().getEnterTransition().addListener(new Transition.TransitionListener() {
            @Override
            public void onTransitionStart(Transition transition) {
                Animator circularReveal = ViewAnimationUtils.createCircularReveal(image_bg, image_bg.getWidth() / 2, image_bg.getHeight() / 2
                        , icon_gg.getWidth()/2, Math.max(image_bg.getWidth(), image_bg.getHeight()));
                image_bg.setBackgroundColor(Color.BLACK);
                circularReveal.setDuration(600);
                circularReveal.start();
            }

            @Override
            public void onTransitionEnd(Transition transition) {
                fab.animate()
                        .scaleY(1)
                        .scaleX(1)
                        .start();
            }

            @Override
            public void onTransitionCancel(Transition transition) {

            }

            @Override
            public void onTransitionPause(Transition transition) {

            }

            @Override
            public void onTransitionResume(Transition transition) {

            }
        });

    }

    @Override
    public void onBackPressed() {
        finishAfterTransition();
        super.onBackPressed();

    }
}

由此可见 :动画效果都在XML中实现了,Activity只要加载先关布局即可

代码:animatedTransitionsLearn-master

Transition系列文章
一、初识Transition—实现两个场景的变换
二、番外篇 Transition之ViewOverlay
三、定义 界面指定元素 或界面间共享元素 的转场动画基础
四、Content Transition实现非共享元素转场
五、SharedElementTransition之Activity间的转场
六、SharedElementTransition之Fragment间的转场
七、番外篇- 自定义Visibility
八、5.0以下实现共享转场


本篇参考 :
深入理解Content Transition :建议去了解一下
animatedTransitionsLearn-master
Android转场动画

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,519评论 25 707
  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 12,693评论 2 59
  • 假设,两个有Activity A和B A启动B: A发生exit动画,B发生enter动画 B返回A:B发生ret...
    i冰点阅读 17,224评论 7 42
  • 晨起鸟鸣路人行,醒来囫囵急无影。 袅袅细烟众车堵,漫漫拼搏谁人顾。
    爱懵的兔子阅读 119评论 2 1
  • 前端调微信接口->微信调后台->后台获取openid->返回openid给前端
    zhangtaiwei阅读 371评论 0 1