在上一篇中我们学习了属性动画的ObjectAnimator使用,不了解的可以看看 Android属性动画Property Animation系列一之ObjectAnimator。这一篇我们来学点新的东西。做项目的时候应该碰到这种问题:根据不同条件显示或者隐藏一个控件或者布局,我们能想到的第一个方法就是 调用View.setVisibility()方法。虽然实现了显示隐藏效果,但是总感觉这样的显示隐藏过程很僵硬,让人不是很舒服,那么有没有办法能让这种显示隐藏有个过渡的动画效果呢?答案是肯定的,不言而喻就是LayoutTransition类了。
上效果图
这里写图片描述

实现上面的效果只需要在布局容器中添加android:animateLayoutChanges=”true”属性就ok了。不信你看
布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="buttonClick"
            android:text="添加控件" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="buttonClick1"
            android:text="移除控件" />
    </LinearLayout>


    <LinearLayout
        android:id="@+id/parent"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:animateLayoutChanges="true"
        android:orientation="vertical"></LinearLayout>

</LinearLayout>

如果布局容器中为添加android:animateLayoutChanges=”true”默认是么有动画效果的。该属性只能用在ViewGroup控件里,表示容器里面布局改变时有默认的动画效果,比如说添加控件,删除控件的时候就是默认的动画效果。

以上动画调用代码如下:

package com.xjp.animations;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;

/**
 * Description:
 * User: xjp
 * Date: 2015/5/22
 * Time: 15:06
 */

public class LayoutAnimationActivity extends Activity {

    private LinearLayout parent;
    private int i = 0;
    private int j = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_layout_animation);

        parent = (LinearLayout) findViewById(R.id.parent);
    }

    public void buttonClick(View view) {
        addButtonView();
    }

    public void buttonClick1(View view) {
        removeButtonView();
    }

    private void addButtonView() {
        i++;
        Button button = new Button(this);
        button.setText("button" + i);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
        parent.addView(button, params);
    }

    private void removeButtonView() {
        if (i > 0)
            parent.removeViewAt(0);
    }
}

是不是发现很简单?除了布局中添加了android:animateLayoutChanges=”true”属性,代码中根本不需要做任何处理就有很舒服的过渡动画效果了。主要是添加了该属性之后在容器中有布局改变时系统会默认给该容器添加一个默认的动画效果。如果你觉得上面的的效果已经很nice~了,那你就太容易满足了,我们除了使用系统默认的布局容器动画,还可以自定义布局容器动画。现在该LayoutTransition类上场了。

LayoutTransition

该类用于当前布局容器中有View添加,删除,隐藏,显示的时候定义布局容器自身的动画和View的动画。也就是说当一个LinerLayout中隐藏一个view的时候,我们可以自定义 整个LinerLayout容器因为隐藏了view而改变的动画,同时还可以自定义被隐藏的view自己消失时候的动画。你可以先new一个LayoutTransition对象,通过setLayoutTransition()方法将对象设置进一个布局容器ViewGroup中去。代码如下:

    private LinearLayout container;
    private LayoutTransition mTransitioner;
 /**
     * 初始化容器动画
     */
    private void initTransition() {
        mTransitioner = new LayoutTransition();
        container.setLayoutTransition(mTransitioner);
    }

LayoutTransition类定义了如下几种布局容器动画类型。

  • APPEARING :当view出现或者添加的时候,view出现的动画
  • DISAPPEARING :当view消失或者隐藏的时候,view消失的动画
  • CHANGE_APPEARING :当添加view导致布局容器改变的时候,整个布局容器的动画
  • CHANGE_DISAPPEARING :当删除或者隐藏view导致布局容器改变的时候,整个布局容器的动画

你可以自定义这些动画,通过setAnimator() 方法把它们设置进一个 LayoutTransition 对象中去。

LayoutTransition的用法

APPEARING–view出现的动画

/**
         * view出现时 view自身的动画效果
         */
        ObjectAnimator animator1 = ObjectAnimator.ofFloat(null, "rotationY", 0F, 90F, 0F);
        mTransitioner.setAnimator(LayoutTransition.APPEARING, animator1);

定义一个旋转的属性动画,这里将动画对象置空,因为系统内部会将添加的view设置为动画对象。然后调用setAnimator()方法将动画设置进LayoutTransition对象mTransitioner中。

完整动画代码如下,具体解释都有注解

package com.xjp.animations;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.Keyframe;
import android.animation.LayoutTransition;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;

/**
 * Description:布局动画Demo
 * User: xjp
 * Date: 2015/5/22
 * Time: 15:06
 */

public class LayoutAnimationActivity extends Activity {


    private int i = 0;
    private LinearLayout container;
    private LayoutTransition mTransitioner;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_layout_animation);

        container = (LinearLayout) findViewById(R.id.parent);

        initTransition();
        setTransition();
    }

    /**
     * 初始化容器动画
     */
    private void initTransition() {
        mTransitioner = new LayoutTransition();
        container.setLayoutTransition(mTransitioner);
    }


    private void setTransition() {
        /**
         * view出现时 view自身的动画效果
         */
        ObjectAnimator animator1 = ObjectAnimator.ofFloat(null, "rotationY", 90F, 0F).
                setDuration(mTransitioner.getDuration(LayoutTransition.APPEARING));
        mTransitioner.setAnimator(LayoutTransition.APPEARING, animator1);

        /**
         * view 消失时,view自身的动画效果
         */
        ObjectAnimator animator2 = ObjectAnimator.ofFloat(null, "rotationX", 0F, 90F, 0F).
                setDuration(mTransitioner.getDuration(LayoutTransition.DISAPPEARING));
        mTransitioner.setAnimator(LayoutTransition.DISAPPEARING, animator2);

        /**
         * view 动画改变时,布局中的每个子view动画的时间间隔
         */
        mTransitioner.setStagger(LayoutTransition.CHANGE_APPEARING, 30);
        mTransitioner.setStagger(LayoutTransition.CHANGE_DISAPPEARING, 30);


        /**
         * 为什么这里要这么写?具体我也不清楚,ViewGroup源码里面是这么写的,我只是模仿而已
         * 不这么写貌似就没有动画效果了,所以你懂的!
         */
        PropertyValuesHolder pvhLeft =
                PropertyValuesHolder.ofInt("left", 0, 1);
        PropertyValuesHolder pvhTop =
                PropertyValuesHolder.ofInt("top", 0, 1);
        PropertyValuesHolder pvhRight =
                PropertyValuesHolder.ofInt("right", 0, 1);
        PropertyValuesHolder pvhBottom =
                PropertyValuesHolder.ofInt("bottom", 0, 1);


        /**
         * view出现时,导致整个布局改变的动画
         */
        PropertyValuesHolder animator3 = PropertyValuesHolder.ofFloat("scaleX", 1F, 2F, 1F);
        final ObjectAnimator changeIn = ObjectAnimator.ofPropertyValuesHolder(
                this, pvhLeft, pvhTop, pvhRight, pvhBottom, animator3).
                setDuration(mTransitioner.getDuration(LayoutTransition.CHANGE_APPEARING));
        changeIn.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                View view = (View) ((ObjectAnimator) animation).getTarget();
                view.setScaleX(1.0f);
            }
        });
        mTransitioner.setAnimator(LayoutTransition.CHANGE_APPEARING, changeIn);


        /**
         * view消失,导致整个布局改变时的动画
         */
        Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
        Keyframe kf1 = Keyframe.ofFloat(.5f, 2f);
        Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
        PropertyValuesHolder pvhRotation =
                PropertyValuesHolder.ofKeyframe("scaleX", kf0, kf1, kf2);
        final ObjectAnimator changeOut = ObjectAnimator.ofPropertyValuesHolder(
                this, pvhLeft, pvhTop, pvhRight, pvhBottom, pvhRotation).
                setDuration(mTransitioner.getDuration(LayoutTransition.CHANGE_DISAPPEARING));
        changeOut.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                View view = (View) ((ObjectAnimator) animation).getTarget();
                view.setScaleX(1.0f);
            }
        });
        mTransitioner.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, changeOut);
    }


    public void buttonClick(View view) {
        addButtonView();
    }

    public void buttonClick1(View view) {
        removeButtonView();
    }

    private void addButtonView() {
        i++;
        Button button = new Button(this);
        button.setText("button" + i);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
        container.addView(button, Math.min(1, container.getChildCount()), params);
    }

    private void removeButtonView() {
        if (i > 0)
            container.removeViewAt(0);
    }
}

布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="buttonClick"
            android:text="添加控件" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="buttonClick1"
            android:text="移除控件" />
    </LinearLayout>


    <LinearLayout
        android:id="@+id/parent"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:animateLayoutChanges="true"
        android:orientation="vertical"></LinearLayout>

</LinearLayout>

最后来一张效果图:
这里写图片描述

布局动画之layoutAnimation

很多时候我们想要在第一次加载ListView或者GridView的时候能有个动画效果来达到一个很好的过度效果。比如下面的效果
这里写图片描述
实现这种效果只需要在布局中添加android:layoutAnimation=”@anim/layout”属性就好了。接下来我们来看看layout.xml动画怎么实现的?在res/anim目录下新建layout.xml文件,代码如下:

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

android:delay 子类动画时间间隔 (延迟) 70% 也可以是一个浮点数 如“1.2”等
android:animationOrder=”random” 子类的显示方式 random表示随机
android:animationOrder 的取值有
normal 0 默认
reverse 1 倒序
random 2 随机
android:animation=”@anim/left” 表示孩子显示时的具体动画是什么代
在res/anim目录下新建left.xml文件,码如下:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="500"
        android:fromXDelta="100%"
        android:fromYDelta="0"
        android:toXDelta="0"
        android:toYDelta="0" />
    <alpha
        android:duration="500"
        android:fromAlpha="0"
        android:toAlpha="1" />
</set>

这样就实现了如上ListView的动画效果了。

当然我们也可以在代码中实现这种动画效果

private void initAinm() {
        //通过加载XML动画设置文件来创建一个Animation对象;
        Animation animation = AnimationUtils.loadAnimation(this, R.anim.left);
        //得到一个LayoutAnimationController对象;
        LayoutAnimationController lac = new LayoutAnimationController(animation);
        //设置控件显示的顺序;
        lac.setOrder(LayoutAnimationController.ORDER_REVERSE);
        //设置控件显示间隔时间;
        lac.setDelay(1);
        //为ListView设置LayoutAnimationController属性;
        listView.setLayoutAnimation(lac);
    }

通过AnimationUtils.loadAnimation加载item的动画来获得一个Animation对象,然后将Animation对象设置到LayoutAnimationController中来获得LayoutAnimationController对象,配置LayoutAnimationController对象的一些属性。最后将LayoutAnimationController对象设置到ListView中。

注意

layoutAnimation动画不仅仅限于ListView,GridView中,也可用于一切ViewGroup中。具体怎么用就看项目需求了。

总结

Android属性动画至此就基本介绍完毕了。其中基本的动画使用和实例也贴出来了,基本能满足日常开发需求,当然有更炫的动画时可以根据这些基础去实现。这么说,以后都可以不用补间动画了?貌似属性动画更加合理,因为这种动画改变的是属性而不仅仅是位置。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐