美文网首页框架【库】
Android Transition Framework(过渡动

Android Transition Framework(过渡动

作者: ZSGZ_AD | 来源:发表于2018-08-27 22:46 被阅读128次

    先说下什么是 Transition(过渡动画). Lollipop(5.0) 中 Activity 和 Fragment 的过渡动画是基于 Android 一个叫作 Transition 的新特性实现的。 初次引入这个特性是在 KitKat(4.4) 中,Transition 框架提供了一个方便的 API 来构建应用中不同 UI 状态切换时的动画。 这个框架始终围绕两个关键概念:场景和过渡。 场景 描述应用中 UI 的状态(这个定义太抽象了,下面会具体解释),过渡 就是确定两个场景转换之间的过渡动画。

    具体作用:

    • 可以在界面(Activity & Fragment)之间跳转的时候添加动画
    • 动画共享元素之间的转换活动
    • 界面中布局元素的过渡动画。

    1. Transitions between Activity & Fragment(Content Transition)

    在 Android 5.0 中, 切换 Activitys 或者 Fragments 时可以使用 Transitions 来构建精致的转场动画。虽然在之前的版本中已经引入 Activity 和 Fragment 的切换动画(通过 Activity#overridePendingTransition() 和 FragmentTransaction#setCustomAnimation() 方法实现),但是动画的对象只能是Activity/Fragment整体。而新的 API 将这个特性延伸,可以协调 Activity/Fragment 中每一个 view ,为其设置单独的的进入和退出 transition,轻松搞定流畅的屏幕切换动作。

    android.transition预定义了三种过渡动画:Explode,Slide和Fade。

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

    不同的场景下,我们可以为同一个界面设置不同效果的过渡动画,这里解释下场景的意思:

    假设 A 和 B 是两个 Activity,通过 A 来启动 B。 A 叫做 "调用Activity"(调用 startActivity() 的那个) B 就是 "被调用Activity"

    根据上述情景,可以为activity划分出4种场景的动画:

    • Activity A 的 退出动画(ExitTransition ),即 A 启动 B 时 A 中 View 的动画
    • Activity B 的 进入动画(EnterTransition ),即 A 启动 B 时 B 中 View 的动画
    • Activity B 的 返回动画(ReturnTransition),即 B 返回 A 时 B 中 View 的动画
    • Activity A 的 重入动画(ReenterTransition), 即 B 返回 A 时 A 中 View 的动画

    实现方法

    接下来我们实际演示下为Activity添加过场动画的实现步骤,可通过xml和代码两种方式实现,以添加Fade动画为例:

    1. 如果是xml方式实现,首先在/res下创建transition文件夹。
      res/transition/slide_from_right
    <?xml version = 1.0 encoding = "utf-8"?>
    <transitionSet xmls:android = "http://schemas.android.com/apk/res/android">
    
    <slide duration = "500"
           slideEage = "left"/>
    
    </transitionSet>
    

    然后在values/styles.xml中创建带动画的主题,再加到Manifest.xml中就可以了:

    <style name="TransitionAppTheme" parent="AppTheme">
            <item name="android:windowContentTransitions">true</item>
            <item name="android:windowEnterTransition">@transition/slide_from_right</item>
            <item name="android:windowExitTransition">@transition/slide_from_right</item>
            <!--避免跳转的Activity动画重叠,则下面两个属性设为false-->
            <item name="android:windowAllowEnterTransitionOverlap">false</item>
            <item name="android:windowAllowReturnTransitionOverlap">false</item>
        </style>
    
    1. 如果直接用代码实现,可以如下实现
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        ... ...
        
        Slide slideTracition = newSlide();
        slideTracition.setSlideEdge(Gravity.LEFT);
        slideTracition.setDuration(getResources().getInteger(R.integer.anim_duration_long));
    
        //也可以直接取res中的transition资源
        //Transition slideTracition = TransitionInflater.from(this).inflateTransition(R.transition.slide_from_left);
        getWindow().setEnterTransition(slideTracition);
        getWindow().setExitTransition(slideTracition);
        
         ... ...
     }
    

    最后,启动动画

    //跳转
    Bundle bundle = ActivityOptions.makeSceneTransitionAnimation(activity).toBundle()
    startActivity(i, bundle);
    //返回
    finshAfterTransition();
    

    上面我们只添加了EnterTransition和ExitTransition,如果未设置ReturnTransition和ReenterTransition的话,后两者分别为前两者的反向动画。
    EnterTransition < - > ReturnTransition
    ExitTransition < - > ReenterTransition

    2.Share elements between Activity(元素共享)

    共享元素过渡动画的背后是通过过渡动画将两个不同布局中的不同view关联起来。Transition框架知道用适当的动画向用户展示从一个view向另外 一个view过度。请记住:共享元素过渡的过程中,view并没有真正从一个布局跑到另外一个布局,整个过程基本都是在后一个布局中完成的。


    image

    元素共享的实现

    1. 允许过渡动画
      需要在/res/style.xml添加
    <item name="android:windowContentTransitions">true</item>
    
    1. 在对应的xml文件指定TransitionName属性
      例如,我们指定activityA中的ImageView和activityB中的TextView为共享元素:
    <ImageView
            android:id="@+id/square_blue"
            style="@style/MaterialAnimations.Icon.Big"
            android:src="@drawable/circle_24dp"
            android:transitionName="@string/square_blue_name" />
    
    <TextView
           android:id="@+id/title"   
           style="@style/MaterialAnimations.TextAppearance.Title.Invers"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_gravity="center_vertical|start"
           android:text="@{sharedSample.name}"
           android:transitionName="@string/sample_blue_title" />
    

    注意:这里必须为共享的俩个元素指定同一个TransitionName,不然不会出现共享效果

    1. 启动Activity
    Intent intent = new Intent(activity,target);
    ActivityOptionCompat option = ActiviyoptionCampat.makeSceneTransitionAnimation(activity,  
    new Pair<View, String>(viewHolder.binding.sampleIcon, activity.getString(R.string.square_blue_name)),
    new Pair<View, String>(viewHolder.binding.sampleName, activity.getString(R.string.sample_blue_title)));
    
    startActivity(intent,option.toBundle());
    

    效果如下:



     

    共享元素动画的效果设置

    我们可以发现对于转场动画(Content transitions) 是根据每个过渡视图的可见性变化来调节的,而共享元素 transition 是根据每个共享元素视图的位置,大小和外观的变化来调节的。与转场动画类似,从 API 21 开始,框架提供了 几个自定义共享元素场景切换动画的 Transition 实现。

    • ChangeBounds - 捕获共享元素布局边界根据不同构造动画。 ChangeBounds 在共享元素 Transition 中经常使用,大多数共享元素在两个 Activity/Fragment 间会有大小 或/和 位置不同。
    • ChangeTransform - 捕获共享元素缩放和角度, 根据不同构建动画。
    • ChangeClipBounds - 捕获共享元素的 clip bounds (剪辑边界) ,根据不同构建动画。
    • ChangeImageTransform - 捕获共享元素 ImageView 的 变换矩阵( transform matrices) ,根据不同构建动画。结合 ChangeBounds, 可以让 ImageView 无缝的改变大小,形状和 ImageView.ScaleType 。

    设置代码:

    Slide slide = new Slide();
    slide.setDuration(500);
    
    ChangeBounds changeBounds = new ChangeBounds();
    changeBounds.setDuration(500);
    
    getWindow().setEnterTransition(slide);
    getWindow().setSharedElementEnterTransition(changeBounds);
    

    前面的代码我们并没有设置共享元素动画,因为如果我们的Theme使用的是 Material主题,当设置共享元素后会默认添加动画效果。

    过渡动画以及共享元素在Fragment之间的实现

    fragment使用过渡动画和共享元素,与Activity大同小异,也是直接添加Transition对象之后启动,主要是启动方法有所不同

    private void addNextFragment(Sample sample, ImageView blue, boolean b) {
            SharedElementFragment2 elementFragment2 = SharedElementFragment2.newInstance(sample);
            Slide slide = new Slide();
            slide.setDuration(getResources().getInteger(R.integer.anim_duration_medium));
            slide.setSlideEdge(Gravity.RIGHT);
            
            ChangeBounds changeBounds = new ChangeBounds();
            changeBounds.setDuration(getResources().getInteger(R.integer.anim_duration_medium));
            
            elementFragment2.setEnterTransition(slide);
            elementFragment2.setAllowEnterTransitionOverlap(b);
            elementFragment2.setAllowReturnTransitionOverlap(b);
            elementFragment2.setSharedElementEnterTransition(changeBounds);
    
            getFragmentManager().beginTransaction().replace(R.id.sample2_content, elementFragment2).addToBackStack(null).addSharedElement(blue, getString(R.string.square_blue_name)).commit();
        }
    

    效果如下:


    3.TransitionManager 控制动画

    TransitionManager是个很好用的工具,使用TransitionManager,我们给view添加一些简单属性动画的时候只需要得到这个view的根布局,然后设置下view动画之后的状态就可以了,TransitionManager会自动为每个view添加预设置好的属性动画,我们甚至可以用它对一个布局内的一组多个view一块儿添加动画,从而实现较为复杂的联动效果。事实上UI场景切换的效果就是通过它来实现的。

    public class ExampleActivity extends Activity implements View.OnClickListener {
        private ViewGroup mRootView;
        private View mRedBox, mGreenBox, mBlueBox, mBlackBox;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            //mRootView是要添加动画view的根布局
            mRootView = (ViewGroup) findViewById(R.id.layout_root_view);
            mRootView.setOnClickListener(this);
    
            mRedBox = findViewById(R.id.red_box);
            mGreenBox = findViewById(R.id.green_box);
            mBlueBox = findViewById(R.id.blue_box);
            mBlackBox = findViewById(R.id.black_box);
        }
    
        @Override
        public void onClick(View v) {
            TransitionManager.beginDelayedTransition(mRootView, new Fade());
            toggleVisibility(mRedBox, mGreenBox, mBlueBox, mBlackBox);
        }
    
        private static void toggleVisibility(View... views) {
            for (View view : views) {
                boolean isVisible = view.getVisibility() == View.VISIBLE;
                view.setVisibility(isVisible ? View.INVISIBLE : View.VISIBLE);
            }
        }
    }
    

    上面的例子我们为一组四个色块添加了Fade效果的动画,同样可以设置成Slide或者Explode,效果如下:


    对于一些更为复杂的联动动画,TransitionManager还为我们提供了一种类似于切换布局就可以完成动画的方案,极大的简化了我们实现复杂动画

    实现步骤如下:

    1. 定义需要切换 layout xml页面;
    2. 调用 Scene.getSceneForLayout() 保存每个Layout;
    3. 调用 TransitionManager.go(scene1, new ChangeBounds()) 切换。
      代码示例:
    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;
        }
    }
    

    4.CircularReveal 显示或隐藏 的效果

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



    参考说明:

    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 中都还不常见,但是效果真的很不错,很值得研究下。

    参考及拓展阅读:
    在 Activity 和 Fragment 中使用 Transition (part 1)
    深入理解Content Transition (part 2)
    深入理解 Shared Element Transition (part 3a)
    延迟共享元素的过渡动画 (part 3b)
    Android共享元素转场动画兼容实践
    【Transition】Android炫酷的Activity切换效果,共享元素

    相关文章

      网友评论

      • yemoumou:俱怀逸兴壮思飞,欲上青天览明月。-简书朋友你好,我是币圈一老友,我的写作方向是区块链和数字货币,初到简书,望多多关照。互粉互赞,已赞,期待您的回赞哦。-迦

      本文标题:Android Transition Framework(过渡动

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