[Andoird][Animation]

1.View Animation(Tweened Animation)补间动画

View Animation主要是针对View的,而且它只是让View展示指定动画,但是并不会改变View的属性。也就是说使用tweened动画来实现移动,缩放,旋转,透明度,最后都是没有改变view的位置,大小,角度,透明度,我们看到的只是一个动画效果。

1).创建动画对象

(1)直接new

Android提供了四个类TranslateAnimation,ScaleAnimation,RotateAnimation和AlphaAnimation。我们可以通过这四个类实现移动,缩放,旋转和透明度设置。如果一个View要同时执行多个动画可以使用AnimationSet。
例子一:TranslateAnimation

    /**
     * 确实如果使用TranslateAnimation实现位移动画,如果没有setX,setY,那么view是不会移动到指定位置的,
     * 而且通过setX,setY移动view到知道位置的时候,view是会闪一下,如果使用tweened动画来实现缩放,旋转,透明度,最后都是没有改变view的,我们看到的只是一个动画效果
     */
    private void transition() {
        UiUtil.initialize(this);
        animation = new TranslateAnimation(0, UiUtil.getScreenWidth() / 2, 0,
                UiUtil.getScreenHeight() / 2);
        animation.setDuration(2000);
        animation.setAnimationListener(new AnimationListener() {

            @Override
            public void onAnimationStart(Animation animation) {
                Log.i("lgy",
                        "start----x:" + button.getX() + " y:" + button.getY());
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
                // TODO Auto-generated method stub

            }

            @Override
            public void onAnimationEnd(Animation animation) {
                button.setX(UiUtil.getScreenWidth() / 2);
                button.setY(UiUtil.getScreenHeight() / 2);
                Log.i("lgy",
                        "end----x:" + button.getX() + " y:" + button.getY());
            }
        });
        button.startAnimation(animation);
    }

例子二:ScaleAnimation

    /**
     * float fromX 动画起始时 X坐标上的伸缩尺寸 
     * float toX 动画结束时 X坐标上的伸缩尺寸 
     * float fromY 动画起始时Y坐标上的伸缩尺寸 
     * float toY 动画结束时Y坐标上的伸缩尺寸 
     * int pivotXType 动画在X轴相对于控件位置类型 
     * float pivotXValue 动画相对于控件的X坐标的开始位置 
     * int pivotYType 动画在Y轴相对于控件位置类型 
     * float pivotYValue 动画相对于控件的Y坐标的开始位置
     * 1.四个参数的ScaleAnimation默认是动画是从左上角开始,
     * 所以new ScaleAnimation(0.0f, 1.4f, 0.0f, 1.4f),
     * 你会看到的效果是动画会以view的左上角为定点,开始向x轴和y轴慢慢伸长到1.4倍,
     * 动画结束后,由于tweened动画不改变view的属性,view就会恢复为正常状态
     * 2.八个参数的ScaleAnimation可以定义相对于控件位置类型 
     * (1)RELATIVE_TO_SELF 相对于自己
     * (2)RELATIVE_TO_PARENT 相对于父控件
     * (3)ABSOLUTE 绝对位置 ,例如Animation.ABSOLUTE, new ScaleAnimation(0.0f, 1.4f, 0.0f, 1.4f, 
                Animation.ABSOLUTE, 200f, Animation.ABSOLUTE, 100f)意思是相对于当前位置
                x轴增量为200的坐标点,y轴增量为100的坐标点
            如果使用的是RELATIVE_TO_SELF,RELATIVE_TO_PARENT类型,那么他的参数表示的的是倍数
            如果使用的是ABSOLUTE,那么他表示的就是具体的增量
     * 
     */
    private void scale() {
        UiUtil.initialize(this);
//      scaleAnimation = new ScaleAnimation(0.0f, 1.4f, 0.0f, 1.4f);
//      scaleAnimation = new ScaleAnimation(0.0f, 1.4f, 0.0f, 1.4f, 
//              Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        scaleAnimation = new ScaleAnimation(0.0f, 1.4f, 0.0f, 1.4f, 
                Animation.RELATIVE_TO_SELF, 1f, Animation.RELATIVE_TO_SELF, 1f);
        scaleAnimation.setDuration(2000);
        scaleAnimation.setAnimationListener(new AnimationListener() {

            @Override
            public void onAnimationStart(Animation animation) {
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
                // TODO Auto-generated method stub

            }

            @Override
            public void onAnimationEnd(Animation animation) {
            }
        });
        button.startAnimation(scaleAnimation);
    }

例子三:RotateAnimation

    /**
     * float fromDegrees:旋转的开始角度。 
     * float toDegrees:旋转的结束角度。 
     * int pivotXType:X轴上旋转点模式,可以取值为ABSOLUTE、RELATIVE_TO_SELF、RELATIVE_TO_PARENT。 
     * float pivotXValue:X轴上旋转点的值。
     * int pivotYType:Y轴上旋转点模式,可以取值为ABSOLUTE、RELATIVE_TO_SELF、RELATIVE_TO_PARENT。 
     * float pivotYValue:Y轴上旋转点的值。
     */
    private void rotate() {
        UiUtil.initialize(this);
        rotateAnimation = new RotateAnimation(0f,360f,Animation.RELATIVE_TO_SELF, 
                0.5f,Animation.RELATIVE_TO_SELF,0.5f); 
        rotateAnimation.setDuration(2000);
        rotateAnimation.setAnimationListener(new AnimationListener() {

            @Override
            public void onAnimationStart(Animation animation) {
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
                // TODO Auto-generated method stub

            }

            @Override
            public void onAnimationEnd(Animation animation) {
            }
        });
        button.startAnimation(rotateAnimation);
    }

例子四:AlphaAnimation

    /**
     * float fromAlpha:开始时候的透明度。 
     * float toAlpha:结束时候的透明度。 
     * 这里都是用倍数表示
     */
    private void alpha() {
        UiUtil.initialize(this);
        alphaAnimation = new AlphaAnimation(0f, 1f); 
        alphaAnimation.setDuration(2000);
        alphaAnimation.setAnimationListener(new AnimationListener() {

            @Override
            public void onAnimationStart(Animation animation) {
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
                // TODO Auto-generated method stub

            }

            @Override
            public void onAnimationEnd(Animation animation) {
            }
        });
        button.startAnimation(alphaAnimation);
    }

例子五:AnimationSet

    /**
     * 同时执行多个动画
     */
    private void animSet() {
        UiUtil.initialize(this);
        animationSet = new AnimationSet(true);
        alphaAnimation = new AlphaAnimation(0f, 1f); 
        animationSet.addAnimation(alphaAnimation);
        rotateAnimation = new RotateAnimation(0f,360f,Animation.RELATIVE_TO_SELF, 
                0.5f,Animation.RELATIVE_TO_SELF,0.5f); 
        animationSet.addAnimation(rotateAnimation);
        scaleAnimation = new ScaleAnimation(0.0f, 1.4f, 0.0f, 1.4f, 
                Animation.RELATIVE_TO_SELF, 1f, Animation.RELATIVE_TO_SELF, 1f);
        animationSet.addAnimation(scaleAnimation);
        animation = new TranslateAnimation(0, UiUtil.getScreenWidth() / 2, 0,
                UiUtil.getScreenHeight() / 2);
        animationSet.addAnimation(animation);
        animationSet.setDuration(2000);
        button.startAnimation(animationSet);
    }

(2)AnimationUtils.loadAnimation

也就是通过加载定义好的xml文件来实例化动画对象。
步骤一:在res文件夹下新建一个anim文件夹(anim文件夹下放的是Tweened动画,如果想放Property动画,必须要在res下新建一个animator,把Property动画的文件放里边,在Android Studio里是需要这样区分的,Eclipse没有验证,就不清楚了)
步骤二:在anim里可以定义自己Tweened动画xml文件,他们一般是以set为根标签。

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >

    <translate
        android:fromXDelta="-100%"
        android:toXDelta="0%"
        android:duration="1000" />
</set>

步骤三:加载xml,实例化动画对象

    /**
     * 这里是实现将mContentView从左边移动出现,而mLoadingView向左移动消失的效果
     */
    private void xmlAnim() {
        contentViewAnim = AnimationUtils.loadAnimation(this, R.anim.tweened_anim_translation);
        contentViewAnim.setAnimationListener(new AnimationListener() {
            
            @Override
            public void onAnimationStart(Animation animation) {
                // TODO Auto-generated method stub
                
            }
            
            @Override
            public void onAnimationRepeat(Animation animation) {
                // TODO Auto-generated method stub
                
            }
            
            @Override
            public void onAnimationEnd(Animation animation) {
                mContentView.setVisibility(View.VISIBLE);
            }
        });
        mContentView.startAnimation(contentViewAnim);
        loadingViewAnim = AnimationUtils.loadAnimation(this, R.anim.tweened_anim_translation2);
        loadingViewAnim.setAnimationListener(new AnimationListener() {
            
            @Override
            public void onAnimationStart(Animation animation) {
                // TODO Auto-generated method stub
                
            }
            
            @Override
            public void onAnimationRepeat(Animation animation) {
                // TODO Auto-generated method stub
                
            }
            
            @Override
            public void onAnimationEnd(Animation animation) {
                mLoadingView.setVisibility(View.GONE);
            }
        });
        mLoadingView.startAnimation(loadingViewAnim);
    }

2).动画监听setAnimationListener

我们可以对动画的行为进行监听,根据需要执行我们的操作,AnimationListener这个接口可以监听到动画开始,重复,动画结束的动作。

loadingViewAnim.setAnimationListener(new AnimationListener() {
            
            @Override
            public void onAnimationStart(Animation animation) {
                // TODO Auto-generated method stub
                
            }
            
            @Override
            public void onAnimationRepeat(Animation animation) {
                // TODO Auto-generated method stub
                
            }
            
            @Override
            public void onAnimationEnd(Animation animation) {
                mLoadingView.setVisibility(View.GONE);
            }
        });

3).插值器Imterpolator

其实插值器就是设置动画改变的速率。

  • 1:AccelerateDecelerateInterpolator 加速减速插补器(先慢后快再慢)
  • 2:AccelerateInterpolator 加速插补器(先慢后快)
  • 3:AnticipateInterpolator 向前插补器(先往回跑一点,再加速向前跑)
  • 4:AnticipateOvershootInterpolator 向前向后插补器(先往回跑一点,再向后跑一点,再回到终点)
  • 5:BounceInterpolator 反弹插补器(在动画结束的时候回弹几下,如果是竖直向下运动的话,就是玻璃球下掉弹几下的效果)
  • 6:CycleInterpolator 循环插补器(按指定的路径以指定时间(或者是偏移量)的1/4、变速地执行一遍,再按指定的轨迹的相反反向走1/2的时间,再按指定的路径方向走完剩余的1/4的时间,最后回到原点。假如:默认是让a从原点往东跑100米。它会先往东跑100米,然后往西跑200米,再往东跑100米回到原点。可在代码中指定循环的次数)
  • 7:DecelerateInterpolator 减速插补器(先快后慢)
  • 8:LinearInterpolator 直线插补器(匀速)
  • 9:OvershootInterpolator 超出插补器(向前跑直到越界一点后,再往回跑)
  • 10:FastOutLinearInInterpolator MaterialDesign基于贝塞尔曲线的插补器 效果:依次 慢慢快
  • 11:FastOutSlowInInterpolator MaterialDesign基于贝塞尔曲线的插补器 效果:依次 慢快慢
  • 12:LinearOutSlowInInterpolator MaterialDesign基于贝塞尔曲线的插补器 效果:依次 快慢慢
setInterpolator(new LinearOutSlowInInterpolator())

4).改变属性

我们知道Tweened动画只是让View展示指定动画,但是并不会改变View的属性。那么如果我们要改变它的属性可以通过View的set方法

    /**
     * 确实如果使用TranslateAnimation实现位移动画,如果没有setX,setY,那么view是不会移动到指定位置的,
     * 而且通过setX,setY移动view到知道位置的时候,view是会闪一下,如果使用tweened动画来实现缩放,旋转,透明度,最后都是没有改变view的,我们看到的只是一个动画效果
     */
    private void transition() {
        UiUtil.initialize(this);
        animation = new TranslateAnimation(0, UiUtil.getScreenWidth() / 2, 0,
                UiUtil.getScreenHeight() / 2);
        animation.setDuration(2000);
        animation.setAnimationListener(new AnimationListener() {

            @Override
            public void onAnimationStart(Animation animation) {
                Log.i("lgy",
                        "start----x:" + button.getX() + " y:" + button.getY());
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
                // TODO Auto-generated method stub

            }

            @Override
            public void onAnimationEnd(Animation animation) {
                button.setX(UiUtil.getScreenWidth() / 2);
                button.setY(UiUtil.getScreenHeight() / 2);
//              button.setAlpha(1f);
//              button.setScaleX(1.4f);
//              button.setScaleY(1.4f);
//              button.setRotationX(0);
//              button.setRotationY(100);
                Log.i("lgy",
                        "end----x:" + button.getX() + " y:" + button.getY());
            }
        });
        button.startAnimation(animation);
    }

2.Drawable Animation(帧动画)

这个其实就是按顺序一帧一帧的播放图片。

1)使用xml加载

步骤一:在res下的anim文件夹下创建帧动画xml文件,这个文件的根标签是animation-list。

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"  
    android:oneshot="false">  
    <item android:drawable="@drawable/little_gray_01" android:duration="300" />  
    <item android:drawable="@drawable/little_gray_03" android:duration="300" />  
    <item android:drawable="@drawable/little_gray_04" android:duration="300" />  
    <item android:drawable="@drawable/little_gray_05" android:duration="300" />  
</animation-list> 

步骤二:加载xml,创建对象

    /**
     * ProgressBar可以用下面这个设置
     *  bar.setIndeterminate(true);
        bar.setIndeterminateDrawable(getResources().getDrawable(R.anim.frame_anim_list));       
     */
    private void xmlAnimation()
    {
        imageView.setBackgroundResource(R.anim.frame_anim_list);
        AnimationDrawable animation = (AnimationDrawable) imageView.getBackground();
        animation.start();
    }

2)不使用xml

    private void noXmlAnimation()
    {
        AnimationDrawable anim = new AnimationDrawable();  
        Drawable drawable1 = getResources().getDrawable(R.drawable.little_gray_01); 
        Drawable drawable3 = getResources().getDrawable(R.drawable.little_gray_03); 
        Drawable drawable4 = getResources().getDrawable(R.drawable.little_gray_04); 
        Drawable drawable5 = getResources().getDrawable(R.drawable.little_gray_05); 
        
        anim.addFrame(drawable1, 300);
        anim.addFrame(drawable3, 300); 
        anim.addFrame(drawable4, 300); 
        anim.addFrame(drawable5, 300); 
        anim.setOneShot(false); 
        imageView.setBackground(anim);
        anim.start(); 
    }

3.Property Animation

是在Android 3.0中才引进的,它更改的是对象的实际属性,在View Animation(Tween Animation)中,其改变的是View的绘制效果,真正的View的属性保持不变。而且Property Animation不止可以应用于View,还可以应用于任何对象。Property Animation只是表示一个值在一段时间内的改变,当值改变时要做什么事情完全是你自己决定的。

1)ValueAnimator

ValueAnimator包含Property Animation动画的所有核心功能,它是PropertyAnimation动画的基础,ObjectAnimator也是继承了ValueAnimation。

(1)基础用法

这个例子只修改了view的一个属性:x的坐标

    /**
     * 只修改button的x轴动画
     */
    private void valueAnim()
    {
        UiUtil.initialize(this);
        ValueAnimator animator = ValueAnimator.ofFloat(0f,UiUtil.getScreenWidth()/2);
        animator.addUpdateListener(new AnimatorUpdateListener() {
            
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                button.setTranslationX((Float)animation.getAnimatedValue());
            }
        });
        animator.setDuration(2000);
        animator.start();
    }
(2)修改多个属性

下面展示的是同时修改x坐标和y坐标的值,这里用到了PropertyValuesHolder ,可以使用它来同时执行多个动画。

    /**
     * 修改button的x轴和Y轴动画,通过下面的方法可以传多个动画
     */
    private void valueAnim2()
    {
        UiUtil.initialize(this);
        PropertyValuesHolder xvalue = PropertyValuesHolder.ofFloat("x", 0f,UiUtil.getScreenWidth()/2);
        PropertyValuesHolder yvalue = PropertyValuesHolder.ofFloat("y", 0f,UiUtil.getScreenHeight()/2);
        ValueAnimator animator = ValueAnimator.ofPropertyValuesHolder(xvalue,yvalue);
        animator.addUpdateListener(new AnimatorUpdateListener() {
            
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                button.setTranslationX((Float)animation.getAnimatedValue("x"));
                button.setTranslationY((Float)animation.getAnimatedValue("y"));
            }
        });
        animator.setDuration(2000);
        animator.start();
    }
(3)监听属性的渐变值

可以通过addUpdateListener监听到属性的渐变值,可以看到下面的代码,
我们可以通过PropertyValuesHolder 的ofFloat给某个属性定义一个propertyName,然后在addUpdateListener里的回调方法onAnimationUpdate,可以通过animation.getAnimatedValue(propertyName)获取对于该属性名的渐变值。

    private void valueAnim2()
    {
        UiUtil.initialize(this);
        PropertyValuesHolder xvalue = PropertyValuesHolder.ofFloat("x", 0f,UiUtil.getScreenWidth()/2);
        PropertyValuesHolder yvalue = PropertyValuesHolder.ofFloat("y", 0f,UiUtil.getScreenHeight()/2);
        ValueAnimator animator = ValueAnimator.ofPropertyValuesHolder(xvalue,yvalue);
        animator.addUpdateListener(new AnimatorUpdateListener() {
            
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                button.setTranslationX((Float)animation.getAnimatedValue("x"));
                button.setTranslationY((Float)animation.getAnimatedValue("y"));
            }
        });
        animator.setDuration(2000);
        animator.start();
    }

2)ObjectAnimator

(1)ObjectAnimator的基本用法

也是修改一个属性值。

    /**
     * 修改button的x轴动画
     */
    private void objectAnim()
    {
        UiUtil.initialize(this);
        ObjectAnimator animator = ObjectAnimator
                .ofFloat(button, "translationX", 0f,UiUtil.getScreenWidth()/2);
        animator.setDuration(2000);
        animator.start();
    }
(2)修改多个属性值
    /**
     * 修改button的x轴和Y轴动画,通过下面的方法可以传多个动画
     */
    private void objectAnim2()
    {
        UiUtil.initialize(this);
        PropertyValuesHolder xvalue = PropertyValuesHolder.ofFloat("x", 0f,UiUtil.getScreenWidth()/2);
        PropertyValuesHolder yvalue = PropertyValuesHolder.ofFloat("y", 0f,UiUtil.getScreenHeight()/2);
        ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(button, xvalue,yvalue);
        animator.setDuration(2000);
        animator.start();
    }
(3)加载xml方式执行动画

步骤一:在res的animator目录下创建动画xml文件,这动画很明显,是同时执行将view的x坐标和y坐标都移动到400处。

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:ordering="together" >
 <!-- 动画执行顺序 sequentially:顺序执行;together:同时执行。 -->

    <objectAnimator
        android:propertyName="translationX"
        android:duration="2000"
        android:valueFrom="0"
        android:valueTo="400"
        android:valueType="floatType" />
    <objectAnimator
        android:propertyName="translationY"
        android:valueFrom="0"
        android:duration="2000"
        android:valueTo="400"
        android:valueType="floatType" />
 <!-- 动画值的类型 -->

</set>

步骤二:加载xml文件,执行动画
通过AnimatorInflater.loadAnimator方法可以获得一个Animator 对象。

    /**
     * 修改button的x轴和Y轴动画,通过下面的方法可以传多个动画
     */
    private void objectAnimByXml()
    {
        
        Animator animator = AnimatorInflater.loadAnimator(this, R.animator.property_tran_x);
        animator.setTarget(button);
        animator.start();
    }

3)AnimatorSet 和 PropertyValuesHolder

AnimatorSet 和PropertyValuesHolder都是可以执行多个动画的,PropertyValuesHolder的动画是必须全部同时执行的,而AnimatorSet 可以控制动画是同时执行还是先后执行。

    /**
     * AnimatorSet
     * play和with可以同时播放多个动画
     * play,after,before可以设置动画的先后顺序
     * 而PropertyValuesHolder是无法设置动画的先后播放顺序,只能同时执行多个动画
     */
    private void objectAnimSet()
    {
        UiUtil.initialize(this);
        ObjectAnimator animatorx = ObjectAnimator
                .ofFloat(button, "translationX", 0f,UiUtil.getScreenWidth()/2);
        ObjectAnimator animatory = ObjectAnimator
                .ofFloat(button, "translationY", 0f,UiUtil.getScreenHeight()/2);
        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.play(animatorx).with(animatory);
        animatorSet.setDuration(2000);
        animatorSet.start();
    }

4)ViewPropertyAnimator

ViewPropertyAnimator也是用于同时执行多个动画的,但是它可以直接从view的animate()方法里获得,如果是对view要执行多个动画的情况下,使用它是十分简便的。

    /**
     * ViewPropertyAnimator
     * 如果View只用到一两个动画,那么用ObjectAnimatior就好了,
     * 但是如果一个View同时要执行多个动画ViewPropertyAnimator的方法会更佳
     */
    private void ViewPropertyAnim()
    {
        UiUtil.initialize(this);
        button.animate()
        .translationX(UiUtil.getScreenWidth()/2)
        .translationY(UiUtil.getScreenHeight()/2)
        .setDuration(2000)
        .start();
    }

5)布局改变的动画(Viewgroup)

API 11后,在ViewGroup中添加或删除view会触发系统提供的布局动画。

(1)在xml代码中的ViewGroup中添加android:animateLayoutChanges="true"既可触发系统内置的增加删除item view的动画效果。LinearLayout是继承ViewGroup的这个大家都知道的哦。
<?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:id="@+id/body"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:animateLayoutChanges="true"
        android:layout_weight="1"
        android:showDividers="middle"

        android:orientation="vertical" >
    </LinearLayout>

    <Button
        android:id="@+id/add"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="ADD" />

</LinearLayout>
2)代码中的ViewGroup直接设置setLayoutTransition(new LayoutTransition());即可调用系统提供的默认动画。
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test_linearlaout_anim);
        bodyLayout = (LinearLayout) findViewById(R.id.body);
        bodyLayout.setLayoutTransition(new LayoutTransition());
        add = (Button) findViewById(R.id.add);
        add.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View v) {
                addItem();
            }
        });
    }
(3)自定义ViewGroup增加和删除item的动画

可以通过自己定义LayoutTransition,然后通过setLayoutTransition来设置自己定义的动画。

    /**
     * 自定义的LayoutTransaction是在API11出现的
     * ObjectAnimator outAnimator = new ObjectAnimator().ofFloat(null, "translationX", 0f,UiUtil.getScreenWidth()).setDuration(4000);
     * 上面这样在结尾设置setDuration(4000)动画时间是没有效果的
     * 需要在layoutTransition对象直接设置动画时间才有效果layoutTransition.setDuration(4000)
     */
    private LayoutTransition initCustomLayoutTransition()
    {
        UiUtil.initialize(this);
        LayoutTransition layoutTransition = new LayoutTransition();
        ObjectAnimator outAnimator = ObjectAnimator.ofFloat(null, "translationX", 0f,UiUtil.getScreenWidth()).setDuration(3000);
        layoutTransition.setAnimator(LayoutTransition.DISAPPEARING, outAnimator);
//      layoutTransition.setDuration(4000);
        ObjectAnimator intAnimator = ObjectAnimator.ofFloat(null, "translationX", UiUtil.getScreenWidth(),0f);
        layoutTransition.setAnimator(LayoutTransition.APPEARING, intAnimator);
        //加了下面两个效果感觉不是很好,而且效果也没出来
//      ObjectAnimator intOtherAnimator = ObjectAnimator.ofFloat(null, "rotationY", 0,90,0);
//      layoutTransition.setAnimator(LayoutTransition.CHANGE_APPEARING, intOtherAnimator);
//      
//      ObjectAnimator outOtherAnimator = ObjectAnimator.ofFloat(null, "rotationX", 0,-90,0);
//      layoutTransition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, outOtherAnimator);
        return layoutTransition;
    }
<1>可以定制dViewGroup的几个动画效果

我们可以定义的动画有一下几种:

  • 1.LayoutTransition.APPEARING 子View添加到容器中时的过渡动画效果。
  • 2.LayoutTransition.DISAPPEARING 子View从容器中移除时的过渡动画效果。
  • 3.LayoutTransition.CHANGE_DISAPPEARING 子View从容器中移除时,其它子view位置改变的过渡动画。
  • 4.LayoutTransition.CHANGE_APPEARING 子View添加到容器中时,其他子View位置改变的过渡动画。
  • 5.LayoutTransition.CHANGING 4.1 JellyBean上还有一个增强的功能,可以在容器内的子view的layout发生变化时也播放动画。
    下面这段代码就是设置子View从容器中移除时,其它子view位置改变的过渡动画:
layoutTransition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, outOtherAnimator);
<2>LayoutTransition需要注意的问题

1.LayoutTransition.CHANGE_APPEARING和LayoutTransition.CHANGE_DISAPPEARING必须要使用PropertyValuesHolder构造的动画才有效
2.在构造PropertyValuesHolder动画时,"left","top","right"和"bottom"属性至少要写两个,而且如果是两个的时候,必须是左上,左下,右上,右下的模式,不能出现左右,上下的模式
3.对于LayoutTransition.CHANGE_APPEARING和LayoutTransition.CHANGE_DISAPPEARING,在构造PropertyValuesHolder时,所使用的ofInt,ofFloat中的参数值,第一个值和最后一个值必须相同,不然此属性所对应的的动画将被放弃,在此属性值上将不会有效果

    /**
     * http://blog.csdn.net/harvic880925/article/details/50985596
     * PropertyValuesHolder是用于同时执行多个动画,没必要像下面这样,只有一个动画就不必使用PropertyValuesHolder
     * 但是LayoutTransition有几个需要注意的问题:
     * 1.LayoutTransition.CHANGE_APPEARING和LayoutTransition.CHANGE_DISAPPEARING必须要使用PropertyValuesHolder构造的动画才有效
     * 2.在构造PropertyValuesHolder动画时,"left","top","right"和"bottom"属性至少要写两个,而且如果是两个的时候,必须是左上,左下,右上,右下的模式,不能出现左右,上下的模式
     * 3.对于LayoutTransition.CHANGE_APPEARING和LayoutTransition.CHANGE_DISAPPEARING,在构造PropertyValuesHolder时,所使用的ofInt,ofFloat中的参数值,第一个值和最后一个值必须相同,不然此属性所对应的的动画将被放弃,在此属性值上将不会有效果
     * @return
     */
    private LayoutTransition initCustomLayoutTransition2()
    {
        UiUtil.initialize(this);
        LayoutTransition layoutTransition = new LayoutTransition();
        PropertyValuesHolder outValuesHolder = PropertyValuesHolder.ofFloat("translationX", 0f,UiUtil.getScreenWidth());
        PropertyValuesHolder inValuesHolder = PropertyValuesHolder.ofFloat("translationX", UiUtil.getScreenWidth(),0f);
        ObjectAnimator outAnimator = ObjectAnimator.ofPropertyValuesHolder(bodyLayout,outValuesHolder);
        layoutTransition.setAnimator(LayoutTransition.DISAPPEARING, outAnimator);
        ObjectAnimator intAnimator = ObjectAnimator.ofPropertyValuesHolder(bodyLayout,inValuesHolder);
        layoutTransition.setAnimator(LayoutTransition.APPEARING, intAnimator);
        
        
        PropertyValuesHolder pvhLeft =
                PropertyValuesHolder.ofInt("left", 0, 0);
        PropertyValuesHolder pvhTop =
                PropertyValuesHolder.ofInt("top", 0, 0);
        PropertyValuesHolder pvhRight =
                PropertyValuesHolder.ofInt("right", 0, 0);
        PropertyValuesHolder pvhBottom =
                PropertyValuesHolder.ofInt("bottom", 0, 0);
        PropertyValuesHolder outOtherValuesHolder = PropertyValuesHolder.ofFloat("rotationY", 0,180,0);
        PropertyValuesHolder inOtherValuesHolder = PropertyValuesHolder.ofFloat("rotationX", 0,-180,0);
        ObjectAnimator intOtherAnimator = ObjectAnimator.ofPropertyValuesHolder(bodyLayout, pvhRight, pvhTop,outOtherValuesHolder);
        layoutTransition.setAnimator(LayoutTransition.CHANGE_APPEARING, intOtherAnimator);
        layoutTransition.setDuration(3000);
        ObjectAnimator outOtherAnimator = ObjectAnimator.ofPropertyValuesHolder(bodyLayout, pvhRight, pvhTop,inOtherValuesHolder);
        layoutTransition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, outOtherAnimator);
        return layoutTransition;
    }

4.Viewpager转场动画

Viewpager本来就有转场动画,但是我们还可以通过PageTransformer设置他的转场动画效果。官网的Using ViewPager for Screen Slides也有介绍。
其实我们要做的很简单,只需要自定义一个PageTransformer,然后在通过Viewpager的setPageTransformer方法设置即可。
当我们实现PageTransformer接口的时候,我们需要实现transformPage方法。里面的两个参数很关键:
View view 这个view即我们能看到的界面;
float position 这个是一个动态属性,是用来控制动画的重要参数;
下面我们通过例子来说明这两个参数。
首先,我给Viewpager的几个界面都设置一个id,pager1=111,pager2=222,pager3=333,这样我们就可以知道在切换界面的时候position 参数改变对应的是哪个view。

public class TestViewPagerActivity extends Activity{

    private ViewPager viewPager = null;
    private List<LinearLayout> pagers = null;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test_viewpager);
        viewPager = (ViewPager) findViewById(R.id.view_pager);
        viewPager.setPageTransformer(true, new DepthPageTransformer());
        pagers = new ArrayList<LinearLayout>();
        Pager pager1 = new Pager(this);
        pager1.setId(111);
        pager1.setText("界面一");
        pager1.setBackgroundColor(Color.RED);
        Pager pager2 = new Pager(this);
        pager2.setId(222);
        pager2.setText("界面二");
        pager2.setBackgroundColor(Color.YELLOW);
        Pager pager3 = new Pager(this);
        pager3.setId(333);
        pager3.setText("界面三");
        pager3.setBackgroundColor(Color.BLUE);
        pagers.add(pager1);
        pagers.add(pager2);
        pagers.add(pager3);
        MyAdapter adapter = new MyAdapter();
        adapter.setList(pagers);
        viewPager.setAdapter(adapter);
    }
    
    private class MyAdapter extends PagerAdapter
    {
        private List<LinearLayout> list = null;
        
        
        public List<LinearLayout> getList() {
            return list;
        }

        public void setList(List<LinearLayout> list) {
            this.list = list;
        }

        @Override
        public int getCount() {

            return list!=null?list.size():0;
        }

        @Override
        public boolean isViewFromObject(View arg0, Object arg1) {

            return arg0==arg1;
        }
        @Override
        public void destroyItem(ViewGroup container, int position,
                Object object) {
            container.removeView(list.get(position));
        }
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            container.addView(list.get(position));
            return list.get(position);
        }
    }
}

然后实现我们自己的PageTransformer,在里面打个日志
Log.i("lgy", "position:"+position+" view:"+view.getId());

/**
 * @author LGY
 *View page :这个当然就是我们在ViewPager中滑动的界面。
 *float position :这个参数是动态改变的,是一个float类型,而不是平常我们所理解的int位置,只有很好的理解这个参数我们才能设计出我们想要的效果。
 *当position=-1时,表示当前页的前一页,此时该页面是看不见的
 *当position= 0时,表示当前页,当前显示页
 *当position=-1时,表示当前页的下一页
 *position取值为 [-Infinity,-1) -> 页面不可见
 *position取值为(1,+Infinity] -> 页面不可见
 *position取值为[-1,1] -> 于可见状态区间
  如果前一页和下一页基本各在屏幕占一半时,前一页的position是-0.5,后一页的posiotn是0.5,所以根据position的值我们就可以自行设置需要的alpha,x/y信息 
 */
public class DepthPageTransformer implements ViewPager.PageTransformer {
    private static final float MIN_SCALE = 0.75f;

    public void transformPage(View view, float position) {
        int pageWidth = view.getWidth();
        Log.i("lgy", "position:"+position+" view:"+view.getId());
        
        if (position < -1) { // [-Infinity,-1)[负无穷大,-1)
            // This page is way off-screen to the left.这一页是屏幕左边
            view.setAlpha(0);

        } else if (position <= 0) { // [-1,0]
            // Use the default slide transition when moving to the left page
            //当向左移动页面的时候,使用默认的幻灯片过渡
            view.setAlpha(1);
            view.setTranslationX(0);
            view.setScaleX(1);
            view.setScaleY(1);

        } else if (position <= 1) { // (0,1]
            // Fade the page out.页面消失了
            view.setAlpha(1 - position);

            // Counteract the default slide transition
            view.setTranslationX(pageWidth * -position);

            // Scale the page down (between MIN_SCALE and 1)
            float scaleFactor = MIN_SCALE
                    + (1 - MIN_SCALE) * (1 - Math.abs(position));
            view.setScaleX(scaleFactor);
            view.setScaleY(scaleFactor);

        } else { // (1,+Infinity]
            // This page is way off-screen to the right.
            view.setAlpha(0);
        }
    }
}

结果:当我向左滑动,界面一切换到显示界面二的时候的日志如下:

切换界面时transformPage的日志

从结果我们可以看出,在切换过程中界面一和界面二我们都可以看到,所以从日志里我们可以看到界面一和界面二的position参数。我们可以再看下面这个图理解

解析position参数

这个图展示的是当界面一滑动到界面的一半的时候,界面一的拿到的position是-0.5,而界面而的position是0.5。
所以根据这个position参数就可以设置界面二的出现动画,如下的代码,我们知道界面二出现的过程中,position是从1到0这样变化的,所以view.setAlpha(1 - position);透明度是从0-1变化。

else if (position <= 1) { // (0,1]
            // Fade the page out.页面消失了
            view.setAlpha(1 - position);

            // Counteract the default slide transition
            view.setTranslationX(pageWidth * -position);

            // Scale the page down (between MIN_SCALE and 1)
            float scaleFactor = MIN_SCALE
                    + (1 - MIN_SCALE) * (1 - Math.abs(position));
            view.setScaleX(scaleFactor);
            view.setScaleY(scaleFactor);

        }

5.总结

其实还有好多细节没有点 出来,由于没有用到,所以就不探究那么仔细了。

6.参考文章

官方介绍动画的使用
Viewpager动画,这篇文章挺好的
http://blog.csdn.net/yegongheng/article/details/38435553
http://blog.csdn.net/javazejian/article/details/52571779
https://developer.android.com/guide/topics/graphics/view-animation.html
https://developer.android.com/guide/topics/graphics/prop-animation.html
https://developer.android.com/guide/topics/graphics/drawable-animation.html

7.源码地址

官方介绍动画的使用Demo
我的例子

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • 1 背景 不能只分析源码呀,分析的同时也要整理归纳基础知识,刚好有人微博私信让全面说说Android的动画,所以今...
    未聞椛洺阅读 2,688评论 0 10
  • 3.0以前,android支持两种动画模式,tween animation,frame animation,在an...
    Ten_Minutes阅读 1,648评论 0 4
  • 动画类型 View Animation(Tween Animation 补间动画)只能支持简单的缩放、平移、旋转、...
    Jinwong阅读 981评论 0 8
  • 如果生活分为正数、0、负数。那么我现在的生活就是负数。每天白天都是喜笑颜开,因为在这苟且的生活里没人愿意看你整天耷...
    晓苦Bi阅读 250评论 0 1
  • 曾经年少,不知愁。饿了,渴了,就找大人要吃的与喝的、玩累了,或困了,就睡。或结伴玩耍,或独自玩,或欢欢喜喜,或哭哭...
    牧野俊风阅读 350评论 2 13