美文网首页android系统控件Android 动画Android
Android动画总结——布局动画、转场动画

Android动画总结——布局动画、转场动画

作者: xiaoyanger | 来源:发表于2017-03-29 11:37 被阅读2015次

    之前一篇文章总结了View动画、属性动画、帧动画,这篇文章继续总结布局动画、转场动画。


    一、布局动画

    布局动画的作用于ViewGroup,执行动画效果的是内部的子View。布局动画在android中可以通过LayoutAnimationLayoutTransition来实现。

    1.LayoutAnimation

    LayoutAnimation实际上是一个View动画,用来控制子View显示时的动画效果。可以通过Java代码或者Xml文件来定义LayoutAnimation动画。

    (1)通过Java代码来定义LayoutAnimation

    定义子View的显示动画layout_item_anim_set.xml

    <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:duration="300"
         android:interpolator="@android:anim/accelerate_interpolator"
         android:shareInterpolator="true">
    
        <alpha
            android:fromAlpha="0.0"
            android:toAlpha="1.0"/>
        <translate
            android:fromXDelta="500"
            android:toXDelta="0"/>
    </set>
    

    ListView为例,给ListView设置item的显示动画:

    private void setLayoutAnimation() {
        Animation animation = AnimationUtils.loadAnimation(this, R.anim.layout_item_anim_set);
        LayoutAnimationController controller = new LayoutAnimationController(animation);
        controller.setDelay(0.5f);
        controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
        mListView.setLayoutAnimation(controller);
    }
    
    (2)通过Xml代码来定义LayoutAnimation:

    layout_anim.xml

    <?xml version="1.0" encoding="utf-8"?>
    <layoutAnimation
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:animation="@anim/layout_item_anim_set"
        android:animationOrder="normal"
        android:delay="0.5">
    </layoutAnimation>
    

    ListView所在布局中调用:

    <ListView
        android:id="@+id/list_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:divider="#f1f1f1"
        android:dividerHeight="1dp"
        android:layoutAnimation="@anim/layout_anim"
        android:listSelector="?android:attr/selectableItemBackground"/>
    

    setDelay()方法以及android:delay属性表示子View动画显示的延迟时间比例。比如动画执行时间是300ms,延迟比例是0.5,那么延迟时间就是150ms,在Listview中,第一个item在延迟150ms开始动画后,第二个在300ms后开始动画,第三个在450ms后开始,以此类推。
    setOder()方法以及android:animationOrder表示动画执行的顺序类型,共有三种:normal表示子View按顺序显示,reverse表示子View按逆序显示,random表示子View随机先后显示。

    LayoutAnimation效果图
    2.LayoutTransition

    LayoutTransition用于在ViewGroup中有子View添加、删除、隐藏、显示时所有子View动画效果。LayoutTransition有5中动画变化形式

    LayoutTransition.APPEARING:子View添加到容器中时的动画效果LayoutTransition.CHANGE_APPEARING:子View添加到容器中时,其他子View位置改变的动画效果
    LayoutTransition.DISAPPEARING:子View被移除时的动画效果
    LayoutTransition.CHANGE_DISAPPEARING:子View被移除时,其他子View的动画效果
    LayoutTransition.CHANGING:子View在容器中位置变化时其他子View的动画效果

    (1)使用默认的动画样式

    只需要在使用的LinearLayout、FrameLayout、RelativeLayout等ViewGroup容器的布局文件中添加android:animateLayoutChanges="true"即可,系统会使用默认的LayoutTransition来实现子View添加、删除或变化是的动画效果。

    默认的LayoutTransition效果
    (2)使用自定义动画样式
    private void init() {
        mContainer = (LinearLayout) findViewById(R.id.container);
        setLayoutTransition();
    }
    
    private void setLayoutTransition() {
        LayoutTransition transition = new LayoutTransition();
        // 子View添加到mContainer时的动画
        Animator appearAnim = ObjectAnimator
                .ofFloat(null, "rotationX", 90, 0)
                .setDuration(transition.getDuration(LayoutTransition.APPEARING));
        transition.setAnimator(LayoutTransition.APPEARING, appearAnim);
        // 子Veiw从mContainer中移除时的动画
        Animator disappearAnim = ObjectAnimator
                .ofFloat(null, "rotationX", 0, 90)
                .setDuration(transition.getDuration(LayoutTransition.DISAPPEARING));
        transition.setAnimator(LayoutTransition.DISAPPEARING, disappearAnim);
        // 子Veiw添加到mContainer中时其他子View的动画
        PropertyValuesHolder pvhLeft = PropertyValuesHolder.ofInt("left", 0, 0);
        PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top", 0, 0);
        PropertyValuesHolder pvhTranslationY = PropertyValuesHolder
                .ofFloat("translationX", 0, 150, 0);
        Animator changeAppearAnim = ObjectAnimator
                .ofPropertyValuesHolder(mContainer, pvhLeft, pvhTop, pvhTranslationY)
                .setDuration(transition.getDuration(LayoutTransition.CHANGE_APPEARING));
        transition.setAnimator(LayoutTransition.CHANGE_APPEARING, changeAppearAnim);
        // 子View从mContainer中移除时其他子View的动画
        PropertyValuesHolder outLeft = PropertyValuesHolder.ofInt("left", 0, 0);
        PropertyValuesHolder outTop = PropertyValuesHolder.ofInt("top", 0, 0);
        PropertyValuesHolder pvhTranslationYDis = PropertyValuesHolder
                .ofFloat("translationX", 0, -150, 0);
        ObjectAnimator changeDisAppearAnim = ObjectAnimator
                .ofPropertyValuesHolder(mContainer, outLeft, outTop, pvhTranslationYDis)
                .setDuration(transition.getDuration(LayoutTransition.CHANGE_DISAPPEARING));
        transition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, changeDisAppearAnim);
    
        mContainer.setLayoutTransition(transition);
    }
    
    // 添加子View到第0个位置
    public void addData(View view) {
        View child = LayoutInflater.from(this)
                .inflate(R.layout.item, mContainer, false);
        mContainer.addView(child, 0);
    }
    
    // 移除第0个子View
    public void deleteData(View view) {
        if (mContainer.getChildCount() != 0) mContainer.removeViewAt(0);
    }
    

    在使用PropertyValuesHolder时,需要注意一下几点:
    1.LayoutTransition.CHANGE_APPEARINGLayoutTransition.CHANGE_DISAPPEARING必须使用PropertyValuesHolder构造动画才会有效果,其他任何方式构造动画都不会有效果。
    2.在使用PropertyValuesHolder时,”left””top”属性就算不需要变化也必须要写,如果不需要变化可以写成:

    PropertyValuesHolder pvhLeft = PropertyValuesHolder.ofInt("left", 0, 0);
    PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top", 0, 0);
    

    3.ofIntofFloat中的参数值,第一个值和最后一个值必须相同,不然此属性所对应的的动画将被放弃,在此属性值上将不会有效果:

    PropertyValuesHolder pvhTranslationY = PropertyValuesHolder
                .ofFloat("translationX", 0, 150, 0);
    

    4.使用的ofIntofFloat中,如果所有参数值都相同,也将不会有动画效果。

    PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top", 0, 0);
    

    动画的参数全部相同,left这个属性的动画不会有任何效果。

    说明:关于PropertyValuesHolder几个注意点参考自文章animateLayoutChanges与LayoutTransition

    自定义动画样式配置LayoutTransition效果

    二、转场动画

    1.Android 5.0之前转场动画

    在Android 5.0以前实现转场动画是通过补间动画来实现,通常是在Activity中是overridePendingTransition(int enterAnim, int exitAnim)方法。
    enterAnimexitAnim两个参数对应的是两个View动画:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_anim);
        setStartActivityAnim();
    }
    
    private void setStartActivityAnim() {
        overridePendingTransition(R.anim.activity_right_in, R.anim.activity_left_out);
    }
    

    入场动画activity_right_in.xml

    <?xml version="1.0" encoding="utf-8"?>
    <translate
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="300"
        android:fromXDelta="100%p"
        android:toXDelta="0"/>
    

    出场动画activity_left_out.xml

    <?xml version="1.0" encoding="utf-8"?>
    <translate
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="300"
        android:fromXDelta="0"
        android:toXDelta="-100%p"/>
    

    ActivityonCreate中调用overridePendingTransition方法只对主动启动Activity有效,如果我们返回上一个Activity也需要同样的转场动画,就需要在finish方法也添加上这个方法。

    @Override
    public void finish() {
        super.finish();
        setEndActivityAnim();
    }
    
    private void setEndActivityAnim() {
        overridePendingTransition(R.anim.activity_left_in, R.anim.activity_right_out);
    }
    

    finish时入场动画activity_left_in.xml

    <?xml version="1.0" encoding="utf-8"?>
    <translate
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="300"
        android:fromXDelta="-100%p"
        android:toXDelta="0"/>
    

    finish时出场动画activity_right_out.xml

    <?xml version="1.0" encoding="utf-8"?>
    <translate
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="300"
        android:fromXDelta="0"
        android:toXDelta="100%p"/>
    

    Android 5.0前转场动画效果
    2.Android 5.0之后转场动画 Activity Transition

    Android 5.0之后,谷歌引入了 Activity Transition 来实现交互更加友好的转场动画效果。

    Tansition的类型共有三种:
    进入 —— 决定Activity中的所有视图怎么进入屏幕
    退出 —— 决定Activity中的所有视图怎么退出屏幕
    共享元素 —— 决定两个Activity之间的过渡时怎么共享它们的视图

    进入和退出包含如下动画效果:
    explode(分解) —— 从屏幕中间进或出
    slide(滑动) —— 从屏幕边缘进或出地
    fade(淡出) —— 改变屏幕上视图的不透明度实现添加或移除视图的效果

    共享元素包含如下动画效果:
    changeBounds —— 改变目标视图的布局边界
    changeClipBounds —— 裁剪目标视图边界
    changeTransform —— 改变目标视图的缩放比例和旋转角度
    changeImageTransform —— 改变目标图片的大小和缩放比例

    说明:Tansition分类及动画效果说明参考《Android群英传》

    先来看下Explode(分解)、Slide(滑动)、Fade(淡出)三种转场动画的使用。

    第一个Activity中:

    public void explode(View view) {
        Intent intent = new Intent(this, NextTransitionActivity.class);
        intent.putExtra("flag", "explode");
        startActivity(intent,
                ActivityOptionsCompat.makeSceneTransitionAnimation(this).toBundle());
    }
    
    public void slide(View view) {
        Intent intent = new Intent(this, NextTransitionActivity.class);
        intent.putExtra("flag", "slide");
        startActivity(intent,
                ActivityOptionsCompat.makeSceneTransitionAnimation(this).toBundle());
    }
    
    public void fade(View view) {
        Intent intent = new Intent(this, NextTransitionActivity.class);
        intent.putExtra("flag", "fade");
        startActivity(intent,
                ActivityOptionsCompat.makeSceneTransitionAnimation(this).toBundle());
    }
    

    启动的第二个Activity中:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
        switch (getIntent().getStringExtra("flag")) {
            case "explode":
                getWindow().setEnterTransition(new Explode());
                getWindow().setExitTransition(new Explode());
                break;
            case "slide":
                getWindow().setEnterTransition(new Slide());
                getWindow().setExitTransition(new Slide());
                break;
            case "fade":
                getWindow().setEnterTransition(new Fade());
                getWindow().setExitTransition(new Fade());
                break;
        }
        setContentView(R.layout.activity_next_transition);
    }
    

    在第二个Activity中可以通过在style中配置<item name="android:windowContentTransitions">true</item>
    就不需要调用getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS)


    共享元素转场动画,使用也比较简单:
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        TransitionAdapter.TransitionViewHolder holder = (TransitionAdapter.TransitionViewHolder) view.getTag();
        View shareViewImg = holder.civImg;
        View shareViewName = holder.tvName;
        Intent intent = new Intent(this, NextTransitionActivity.class);
        intent.putExtra("flag", "share");
        startActivity(intent, ActivityOptionsCompat
                .makeSceneTransitionAnimation(this,
                        Pair.create(shareViewImg, "shareView_img"),
                        Pair.create(shareViewName, "shareView_name"))
                .toBundle());
    }
    

    这里使用ListView实现一个列表,列表中的头像和名字作为共享元素。在ListViewitem的布局以及第二个启动的Activity的布局中,被共享的View都需要在布局文件中添加上相同android:transitionName属性,当然也可以在Java代码中通过ViewCompat.setTransitionName(View view, String transitionName)方法来设置共享ViewtransitionName
    我们给头像和名称分别指定android:transitionName="shareView_img"android:transitionName="shareView_name"
    点击item后启动目标Activity时,指定的options参数为:

    ActivityOptionsCompat.makeSceneTransitionAnimation(this,
            Pair.create(shareViewImg,"shareView_img"),
            Pair.create(shareViewName, "shareView_name"))
    .toBundle());
    

    通过options参数,可以利用Pair构造多个共享元素,但是共享元素View的共享名称transitionName必须一一对应。

    共享元素转场动画效果

    源码:https://github.com/xiaoyanger0825/AnimationSummary

    相关文章

      网友评论

      • wo叫天然呆:不错不错,我想问下兼容性的问题,就是4.4的机器,我无法用到Activity Transition把,还是说Activity Transition自己已经解决了兼容问题
        如果需要处理兼容性的问题的话,那可能存在4.4机器上的动效跟5.0以上的不一样对吧,毕竟共享元素这个东西如果自己写的话感觉不是那么好弄..
      • 02c77a29a01d:我在ListView中添加android:animateLayoutChanges="true"属性并不起作用,是什么原因呢?
      • 梦想编织者灬小楠:我在尝试“转场动画共享元素”的这段代码时,编译器报错了~
        从网上查了查,好像Pair需要指明泛型,之后编译通过了...
        ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(MainActivity.this, new Pair<View, String>(shareViewImg, "shareView_img"), new Pair<View, String>(shareViewName, "shareView_name"));
        xiaoyanger: @梦想编织者灬小楠 我这边编译没收报错呢,你是具体报的什么错误呢?Pair通过creat方法应该会根据参数类型判断创建的Pair泛性呢
      • 梦想编织者灬小楠:第一次知道还有布局动画~涨知识了
        xiaoyanger: @梦想编织者灬小楠 布局动画确实很少用到。

      本文标题:Android动画总结——布局动画、转场动画

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