布局动画:LayoutTransition

QuibblerQuibbler 2020-09-22 约 30 分钟 1751 次阅读

布局动画:LayoutTransition

1、LayoutTransition介绍

Layouttransition位于android.animation包中,Google从API 11时引入这个Layouttransition工具类,专门用来处理ViewGroup布局发生变化时对子控件进行动画处理。

比如一组数据在ViewGroup中显示,这个时候如果添加或者删除其中的数据,同时伴随着每个item的动画,这样能够提高用户的体验,但是开发者实现起来有一点复杂(也可以实现)。

使用Layouttransition类就可以方便的对ViewGroup中的所有View item实现统一的动效,看了源码就会一目了然,是在内部对常见的5个Type动画情况进行封装:内部5个Type、5个Animator、5个ObjectAnimator分别处理不同的ViewGroup item动画:

public class LayoutTransition {
        ...
        /**
         * Private bit fields used to set the collection of enabled transition types for
         * mTransitionTypes.
         */
        private static final int FLAG_APPEARING             = 0x01;
        private static final int FLAG_DISAPPEARING          = 0x02;
        private static final int FLAG_CHANGE_APPEARING      = 0x04;
        private static final int FLAG_CHANGE_DISAPPEARING   = 0x08;
        private static final int FLAG_CHANGING              = 0x10;
        
        /**
         * These variables hold the animations that are currently used to run the transition effects.
         * These animations are set to defaults, but can be changed to custom animations by
         * calls to setAnimator().
         */
        private Animator mDisappearingAnim = null;
        private Animator mAppearingAnim = null;
        private Animator mChangingAppearingAnim = null;
        private Animator mChangingDisappearingAnim = null;
        private Animator mChangingAnim = null;
        
        /**
         * These are the default animations, defined in the constructor, that will be used
         * unless the user specifies custom animations.
         */
        private static ObjectAnimator defaultChange;
        private static ObjectAnimator defaultChangeIn;
        private static ObjectAnimator defaultChangeOut;
        private static ObjectAnimator defaultFadeIn;
        private static ObjectAnimator defaultFadeOut;
        
        /**
         * 所有动画使用的默认持续时间300ms.
         */
        private static long DEFAULT_DURATION = 300;
        ...
    }

2、LayoutTransition API

Layouttransition内部管理5个ObjectAnimator分别处理5种Type的动画,所以大部分方法都需要传入Type,内部用switch语句到具体的ObjectAnimator对象进行操作。

2.1、常量

将LayoutTransition应用于ViewGroup的哪些场景,有如下5种常量分别对应5种情况:

/**
     * 由于ViewGroup添加新的元素,其它已有的元素需要应用动画
     */
    public static final int CHANGE_APPEARING = 0;
    
    /**
     * ViewGroup中移除元素,其它元素需要发生变化应用动画
     */
    public static final int CHANGE_DISAPPEARING = 1;
    
    /**
     * 元素在容器中出现时所定义的动画
     */
    public static final int APPEARING = 2;
    
    /**
     * 元素在容器中消失所定义的动画
     */
    public static final int DISAPPEARING = 3;
    
    /**
     * 由于布局发生变化时所应用的动画(不是由元素的添加或删除所引起)
     * 默认关闭,调用enableTransitionType(int)开启
     */
    public static final int CHANGING = 4;

2.2、方法

①添加、删除Child View方法

addChild(ViewGroup parent, View child, boolean changesLayout)
    showChild(ViewGroup parent, View child, int oldVisibility)
    removeChild(ViewGroup parent, View child, boolean changesLayout)
    hideChild(ViewGroup parent, View child, int newVisibility)

②添加/移除LayoutTransition监听器TransitionListener

addTransitionListener(TransitionListener listener)
    removeTransitionListener(TransitionListener listener)

③启用/判断这5种ViewGroup动画有效

disableTransitionType(int transitionType)
    isTransitionTypeEnabled(int transitionType)

④setter和getter方法

//此标志控制CHANGE_APPEARING动画或CHANGE_DISAPPEARING动画是否也会导致默认的更改动画也在父层次结构上运行
    setAnimateParentHierarchy(boolean animateParentHierarchy)
    setAnimator(int transitionType, Animator animator)
    setDuration(int transitionType, long duration)
    setDuration(long duration)
    setInterpolator(int transitionType, TimeInterpolator interpolator)
    setStagger(int transitionType, long duration)
    //针对每个Type设置每个子item的动画间隔
    setStartDelay(int transitionType, long delay)
    
    getAnimator(int transitionType)
    getDuration(int transitionType)
    getInterpolator(int transitionType)
    //获取Type动画子item的动画时间间隔
    getStagger(int transitionType)
    getStartDelay(int transitionType)

3、默认LayoutTransition动画

LayoutTransition默认提供了一种ViewGroup动画实现。

3.1、XML属性:android:animateLayoutChanges

只需要在布局中的ViewGroup节点下加上android:animateLayoutChanges="true"即可实现ViewGroup组件添加删除View显示默认的动画效果。

<GridLayout
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:animateLayoutChanges="false"
        android:background="@android:color/white"
        android:columnCount="3" />

3.2、代码添加动画

创建LayoutTransition实例,并调用ViewGroup的setLayoutTransition(LayoutTransition transition)方法设置给ViewGroup。

LayoutTransition layoutTransition = new LayoutTransition();
    mViewGroup.setLayoutTransition(layoutTransition);

默认情况下ViewGroup中的mTransition为null,所以添加和删除时是不会有任何动画效果的。setLayoutTransition(LayoutTransition transition)方法定义如下:

/ **
    * 为此ViewGroup设置LayoutTransition对象。 
    * 如果LayoutTransition对象不为null,则将根据该LayoutTransition对象中定义的动画
    * 对由于将孩子添加到ViewGroup或从ViewGroup中删除而引起的布局更改进行动画处理。 
    * 默认情况下,过渡对象为null(因此不会对布局更改进行动画处理)。
    * 替换非null过渡将取消先前的过渡(如果当前正在运行)。
    *
    * @param transition LayoutTransition对象将对布局中的更改进行动画处理。值为null表示不会在布局更改上进行任何过渡。
    * @attr 对应ViewGroup的XML布局属性animateLayoutChanges
    * /
    public void setLayoutTransition(LayoutTransition transition) {
        if (mTransition != null) {
            LayoutTransition previousTransition = mTransition;
            previousTransition.cancel();
            previousTransition.removeTransitionListener(mLayoutTransitionListener);
        }
        mTransition = transition;
        if (mTransition != null) {
            mTransition.addTransitionListener(mLayoutTransitionListener);
        }
    }

3.3、示例

演示代码如下,给ViewGroup添加子View:

mAddBtn.setOnClickListener((v) -> {
        Button button = new Button(this/*, null, R.style.ButtonStyle*/);
        button.setBackgroundResource(R.drawable.button_bg);
        button.setText("按钮 " + (count++));
        mViewGroup.addView(button, 0);
    });

从ViewGroup中删除子View,注意还要将count计数器同时减一

mRemoveBtn.setOnClickListener((v) -> {
        if (mViewGroup.getChildCount() > 0) {
            mViewGroup.removeViewAt(0);
            --count;
        }
    });

4、自定义LayoutTransition动画

上面是默认的动画效果,当然开发者也可以自己实现具体的动效让应用与众不同。主要借助PropertyValuesHolder构造ObjectAnimator。

4.1、自定义动画说明

LayoutTransition坑点很多,和ObjectAnimator直接用又有所不一样,看源码设置动画的setAnimator(int transitionType, Animator animator)方法注释说明:

/**
     * Sets the animation used during one of the transition types that may run. Any
     * Animator object can be used, but to be most useful in the context of layout
     * transitions, the animation should either be a ObjectAnimator or a AnimatorSet
     * of animations including PropertyAnimators. Also, these ObjectAnimator objects
     * should be able to get and set values on their target objects automatically. For
     * example, a ObjectAnimator that animates the property "left" is able to set and get the
     * <code>left</code> property from the View objects being animated by the layout
     * transition. The transition works by setting target objects and properties
     * dynamically, according to the pre- and post-layoout values of those objects, so
     * having animations that can handle those properties appropriately will work best
     * for custom animation. The dynamic setting of values is only the case for the
     * CHANGE animations; the APPEARING and DISAPPEARING animations are simply run with
     * the values they have.
     *
     * <p>It is also worth noting that any and all animations (and their underlying
     * PropertyValuesHolder objects) will have their start and end values set according
     * to the pre- and post-layout values. So, for example, a custom animation on "alpha"
     * as the CHANGE_APPEARING animation will inherit the real value of alpha on the target
     * object (presumably 1) as its starting and ending value when the animation begins.
     * Animations which need to use values at the beginning and end that may not match the
     * values queried when the transition begins may need to use a different mechanism
     * than a standard ObjectAnimator object.</p>
     *
     * @param transitionType One of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
     * {@link #CHANGING}, {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the
     * animation whose animator is being set.
     * @param animator The animation being assigned. A value of <code>null</code> means that no
     * animation will be run for the specified transitionType.
     */
    public void setAnimator(int transitionType, Animator animator) {
        switch (transitionType) {
            case CHANGE_APPEARING:
                mChangingAppearingAnim = animator;
                break;
            case CHANGE_DISAPPEARING:
                mChangingDisappearingAnim = animator;
                break;
            case CHANGING:
                mChangingAnim = animator;
                break;
            case APPEARING:
                mAppearingAnim = animator;
                break;
            case DISAPPEARING:
                mDisappearingAnim = animator;
                break;
        }
    }

总结为一下几点(参考《Android自定义控件开发入门与实战》):

①LayoutTransition.CHANGE_APPEARING和LayoutTransition.CHANGE_DISAPPEARING必须使用PropertyValuesHolder构造动画才会有效果,其他任何方式构造动画都不会有效果。

②在使用PropertyValuesHolder时,”left”、”top”基础属性就算不需要变化也必须要写

PropertyValuesHolder leftValuesHolder = PropertyValuesHolder.ofInt("left", 0, 0);
    PropertyValuesHolder topValuesHolder = PropertyValuesHolder.ofInt("top", 0, 0);

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

//首尾值一样,中间插入变化值
    PropertyValuesHolder rotationDisAppearing = PropertyValuesHolder.ofFloat("rotationY", 90f, 0f, 90f);

④使用的ofInt,ofFloat中,如果所有参数值都相同,也将不会有动画效果。所以至少需要三个值,相同的首尾两个值,中间一个变化值。

//无效的动画设置,首尾值一样。
    PropertyValuesHolder leftValuesHolder = PropertyValuesHolder.ofInt("left", 0, 0);

4.2、构造ObjectAnimator

演示如何设置一个item移除DISAPPEARING时的动画。其它几种类型都是举一反三的。

①如果动画复杂,需要构造多个动画。先构造若干PropertyValuesHolder对象

PropertyValuesHolder rotationDisAppearing = PropertyValuesHolder.ofFloat("rotationY", 0f, 90f);
    PropertyValuesHolder alphaDisappearing = PropertyValuesHolder.ofFloat("alpha", 1f, 0f);
    PropertyValuesHolder colorDisappearing = PropertyValuesHolder.ofObject("BackgroundColor", 
                                                                            new ArgbEvaluator(), 
                                                                            Color.parseColor("#84bf96"), 
                                                                            Color.parseColor("#ed1941"));

②使用PropertyValuesHolder对象构造ObjectAnimator实例,详见《属性动画:PropertyValuesHolder》

ObjectAnimator disappearingAnimator = ObjectAnimator.ofPropertyValuesHolder(mViewGroup, 
                                  leftValuesHolder, topValuesHolder, colorDisappearing, rotationDisAppearing, alphaDisappearing);

③构造LayoutTransition对象

LayoutTransition layoutTransition = new LayoutTransition();

④ObjectAnimator动画指定设置给ayoutTransition对象指定的Type变换类型,这里指定DISAPPEARING时进行动画。

layoutTransition.setAnimator(LayoutTransition.DISAPPEARING, disappearingAnimator);

⑤还可以对LayoutTransition进行一些设置

layoutTransition.setDuration(1000);
    layoutTransition.setDuration(LayoutTransition.DISAPPEARING,750);
    layoutTransition.setInterpolator(LayoutTransition.DISAPPEARING, new AccelerateInterpolator());

⑥最后将构造好的LayoutTransition设置给ViewGroup

mViewGroup.setLayoutTransition(layoutTransition);

相关资料:

         Docs > Reference > LayoutTransition">Android Developers > Docs > Reference > LayoutTransition

        Sample project for Android Animations

        Android 动画之LayoutTransition

        布局动画--LayoutTransition

        LayoutAnimation和LayoutTransition

         

相关推荐

精选
ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager
Android

ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager

ViewPager1、ViewPagerandroidx.viewpager.widget.ViewPager,Android中使用非常广泛的控件,可以说是APP必备:首次打开引导页、页面Banner广告等。常用方法:setAdapter() 设置适配器setOffscreenPageLimit() 设置缓存的页面个数,默认是 1setCurrentItem() 跳转到特定的页面setOnPage

1.1k
获取Android内置WebView内核版本
Android

获取Android内置WebView内核版本

获取Android内置WebView内核版本竟然能遇到这样奇葩的事情,网页用的技术过于新颖,以至于只支持高版本Chromium内核的,低版本安卓系统中内置的内核版本较低,无法加载前端页面。1、设置查看在系统设置里 > 应用 > 应用管理 > 显示系统应用,查看WebView组件:2、页面查看通过WebView发起的网络请求,都会带上浏览器的UA,通常页面都可以通过UA判断浏览器的内核版本。这里有两

1.1w
Android 14适配总结
Android

Android 14适配总结

Android 14适配总结毕业工作至今已经适配了三个Android大版本,从Android 11到Android 12、再到Android 13。2023年,Google即将推出的Android 14,上半年已经开始第一批适配。现在,第四个Android版本已经适配完,总结记录一下。1、Android 14计划Google一般会在2月份对外发布预告,同时放出开发者预览版。“拉通”各大平台、厂商以

1.1w