美文网首页
四、Content Transition实现非共享元素转场

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

作者: HungerDeng | 来源:发表于2017-11-26 17:39 被阅读0次

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转场动画

相关文章

网友评论

      本文标题:四、Content Transition实现非共享元素转场

      本文链接:https://www.haomeiwen.com/subject/lmokbxtx.html