【读书笔记】【Android 开发艺术探索】第 7 章 Android 动画深入分析

            Android 动画分为:View 动画、Frame 动画和 ObjectAnimator 动画。

         view 动画是通过对象不断做图像变换(平移、缩放、旋转、透明度)从而产生动画效果;

          Frame 动画通过顺序播放一系列图像从而产生动画效果;

         属性动画通过动态地改变对象的属性从而达到动画效果, API 11 及其以上使用。

一、View 动画

         1、 分为:AlphaAnimation 透明度动画、RotateAnimation 旋转动画、TranslateAnimation 位移动画和 ScaleAnimaiton 缩放动画

          View 动画的动画集合 是AnimatonSet

          例子:

       View view = new View(this);
        // 透明度动画
        AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1.0F);
        alphaAnimation.setDuration(1000);
        view.startAnimation(alphaAnimation);

        // 旋转动画
        /**
         *   0, 360 代表旋转的起始角度
         *   50, 100 代表旋转中心的坐标
         *
         */
        RotateAnimation rotateAnimation = new RotateAnimation(0, 360, 50, 100);
        rotateAnimation.setDuration(1000);
        view.startAnimation(rotateAnimation);

        // 可以通过设置参数控制旋转动画的参考系,厦门就是设置旋转参考系未自身中心点
        RotateAnimation rotateAnimation1 = new RotateAnimation(0, 360,
                RotateAnimation.RELATIVE_TO_SELF, 0.5F,
                RotateAnimation.RELATIVE_TO_SELF, 0.5F);

        // 位移动画
        TranslateAnimation transformation = new TranslateAnimation(0, 200, 2, 200);
        transformation.setDuration(1000);
        transformation.setFillAfter(true);   // 设置定格在动画之后的状态

        // 缩放动画
        ScaleAnimation scaleAnimation1 = new ScaleAnimation(0, 2, 0, 2);
        // 设置缩放的中心点, 下面是选择自身为缩放中心点。
        ScaleAnimation scaleAnimation2 = new ScaleAnimation(0, 1, 0, 1,
                Animation.RELATIVE_TO_SELF, 0.5F,
                Animation.RELATIVE_TO_SELF, 0.5F);

        // 动画集合
        AnimationSet animationSet = new AnimationSet(true);
        animationSet.setDuration(1000);

        animationSet.addAnimation(alphaAnimation);
        animationSet.addAnimation(rotateAnimation);
        animationSet.addAnimation(transformation);


        view.startAnimation(animationSet);

        // 动画监听
        alphaAnimation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationEnd(Animation animation) {

            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });
利用xml 文件,目录在res/anim/... 下




    

    

    

        android:interpolater 插值器,如果不指定,默认是 @android:anim/accelerate_deccelerate_interpolater;

       android:shareInterpolator  表示动画集合中所有的动画是否公用一个插值器,如果不是,则每个子动画都有单独指定所需的插值器或者默认值。

        在 java 代码中载入     

  Animation animation = AnimationUtils.loadAnimation(AnimationActivity.this, R.anim.animation_set);
  mTextView.startAnimation(animation);

          

          2、自定义 View 动画

    继承 Animation , 实现 initialize 和 applayTransformation 方法, 在 initiaze 方法中做一些初始化工作, 在 applayTransformation 中进行相应的矩阵换算。

          3、帧动画

是顺序播放的一组设定好的图片。

使用xml 标签, 目录 res/drawable/ ... 下

                 帧动画比较容易引起 OOM , 所以在使用帧动画是应尽量避免使用过多尺寸较大的图片。

                 在 xml 中



    
    
    
    
    然后将上面的 Drawable 作为 View 的背景并通过 Drawable 来播放动画 

        mStartLayoutAnimationButton.setBackgroundResource(R.drawable.frame_animation);
        AnimationDrawable drawable = (AnimationDrawable) mStartLayoutAnimationButton.getBackground();
        drawable.start();

        二、 View 动画的特殊使用场景

1、LayoutAnimation 布局动画

LayoutAnimaiton 作用于 ViewGroup , 为 ViewGroup 指定一个动画, 它的子元素出场时都会有动画效果。

(1)、在 xml 中使用

          A、在目录 res/anim/...下,定义 LayoutAnimation , 标签是            



                B、 在目录 res/anim... 下,为子元素指定具体的入场动画, 标签是




    

    

           C、为ViewGroup 指定 android:layoutAnimation     

    
    
       除了在 xml 中指定,还可以在 java 代码中通过 LayoutAnimationController 来实现。

        mListView = (ListView) findViewById(R.id.lv_anima);
        Animation animation = AnimationUtils.loadAnimation(this, R.anim.anim_layout);
        LayoutAnimationController controller = new LayoutAnimationController(animation);
        controller.setDelay(0.5f);
        controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
        mListView.setLayoutAnimation(controller);

    LayoutAnimation 也可以使用纯 java 代码实现

private LayoutAnimationController getLayoutAnimation(){
        AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0.0f);
        alphaAnimation.setDuration(200);
        alphaAnimation.setFillAfter(true);
        LayoutAnimationController controller = new LayoutAnimationController(alphaAnimation);
        controller.setInterpolator(new AccelerateInterpolator());
        controller.setOrder(LayoutAnimationController.ORDER_REVERSE);
        return controller;
    }


        注:LayoutAnimation 在做 ViewGroup 生成是调用一次,之后不再起作用。


     2、Activity 的切换效果

       Activity 有默认的切换效果,如果自定义,则用 overridePendingTransition(int enterAnim, int exitAnim).这个方法必须在 startActivity(Intent) 后者 finish() 之后,否则不起作用。

        enterAnim : Activity 被打开时,所需的动画资源 id;

        exitAnim : Activity 被暂停时, 所需的动画资源id;

        启动 Activity 时和退出 Activity 时

  private void startActivityWithAnimation(){
        Intent intent = new Intent(AnimationActivity.this, MainActivity.class);
        startActivity(intent);
        overridePendingTransition(R.anim.activity_enter_anim, R.anim.activity_exit_anim);
    }

    @Override
    public void finish() {
        super.finish();
        overridePendingTransition(R.anim.activity_enter_anim, R.anim.activity_exit_anim);
    }
         如果是Fragment 中使用想要切换的效果使用 setCustomAnimation()


    三、属性动画

           属性动画可以对任意对象的属性进行动画而不仅仅是 View, 动画默认时间是 300 ms, 默认帧率是 10ms/帧,可以达到的效果是:在一个时间间隔内完成对象从一个属性值到另一个属性值的改变。

          属性动画是 API 11及其以上才有,低版本可以使用 NineOldAndroids 兼容使用。

         1、 在  xml 中




  
    
    
    
    
        ...
    
        
          

            属性动画的  xml 文件在目录 res/animator/... 下

            然后再 java 代码中引用        

 AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(AnimationActivity.this, R.animator.property_animatior);
 set.setTarget(mTextView);
 set.start();
      亦可以完全使用 java 代码实现。

 private static  ObjectAnimator getObjectAnimator(final View view, int drution, int translationYStart, int translationYTo){
	    	PropertyValuesHolder translateHolder = PropertyValuesHolder.ofFloat("translationY", translationYStart, translationYTo);
			PropertyValuesHolder alphaHolder =  PropertyValuesHolder.ofFloat("alpha", 0 , 1);
	
			ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(view, alphaHolder, translateHolder).setDuration(drution);
			objectAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
			objectAnimator.addListener(new AnimatorListenerAdapter() {
				@Override
				public void onAnimationStart(Animator animation) {
					super.onAnimationStart(animation);
					view.setVisibility(View.VISIBLE);
				}
			});
			return objectAnimator;
	    }

     2、属性动画的集合 PropertyValuesHolder   

              private static  ObjectAnimator getObjectAnimator(final View view, int drution, int translationYStart, int translationYTo){
	    	PropertyValuesHolder translateHolder = PropertyValuesHolder.ofFloat("translationY", translationYStart, translationYTo);
			PropertyValuesHolder alphaHolder =  PropertyValuesHolder.ofFloat("alpha", 0 , 1);
	
			ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(view, alphaHolder, translateHolder).setDuration(drution);
			objectAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
			objectAnimator.addListener(new AnimatorListenerAdapter() {
				@Override
				public void onAnimationStart(Animator animation) {
					super.onAnimationStart(animation);
					view.setVisibility(View.VISIBLE);
				}
			});
			return objectAnimator;
	    }

         3、TimeInterpolator 和 TypeEvaluator

                TimeInterpolator 时间插值器,系统默认设置为 LinearInterpolator

                TypeEvaluator 类型估值器,根据当前属性改变的百分比来计算改变后的属性值,系统默认设置为 IntEvaluator (针对整型属性)

                自定义插值器需要实现 Interpolator 或者 TimeInterpolator ,自定义估值算法需要实现 TypeEvaluator. 另外就是如果要对其他类型(非 int, float、 Color)做动画,那么必须要自定义类型估值器算法。

                4、属性动画的监听器

 可用 AmiationListener 进行动画全过程的监听,亦可使用 AnimatorListenerAdapter 进行单个选择监听。

                  AnimatorUpdateListner 会监听整个动画过程,动画由许多帧构成的,每播放一帧,onAnimationUpdate 就会被调用一次。                

   public static interface AnimatorUpdateListener {
        void onAnimationUpdate(ValueAnimator animation);
    }

              5、对任意属性做动画

                     对 object 的属性做动画,想要动画生效,要满足两个条件:

      1、object 必须要提供 set 方法,如果动画的时候没有传递初始值,那么还要提供 get 方法;

    2、object 的 set 方法对属性所做的改变必须通过某种方式反映出来,如果不满足,会 crash.

    对于只满足条件 1 而为满足条件 2 用以下三种方式解决

                      I、给对象加上 get 和 set 方法,如果有权限的话;

             II、用一个类来包装原始对象, 间接为其提供 get 和 set 方法;

                     III、采用 ValueAnimator, 监听动画,自己实现属性的改变。

                  

                      II 方法,以 Button 为例         

     /**
     *  用一个类来包装原始对象,间接为其提供 get 和 set 方法
     */
    private void performAnimate1(){
        ViewWrapper viewWrapper = new ViewWrapper(mAnimationButton);
        ObjectAnimator.ofInt(viewWrapper, "width", 700)
                .setDuration(2000)
                .start();
    }

    private static  class ViewWrapper{
        private View mTargetView;

        public ViewWrapper(View view){
            mTargetView = view;
        }

        public int getWidth(){
            return  mTargetView.getLayoutParams().width;
        }

        public void setWidth(int width){
            mTargetView.getLayoutParams().width = width;
            mTargetView.requestLayout();
        }
    }
          III、采用 ValueAnimator, 监听动画过程,自己实现属性的改变            

    private void performAnimate2(final View view , final int start, final int end){
        ValueAnimator valueAnimator = ValueAnimator.ofInt(1, 100);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            // 持有一个 IntEvaluator 对象, 方便下面估值的时候使用
            private IntEvaluator mIntEvaluator = new IntEvaluator();

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {

                // 获得当前动画的进度值, 整型 , 1~ 100 之间
                int currentValue = (Integer) animation.getAnimatedValue();
                Log.i("yxh", "current value: " + currentValue);

                // 获得当前进度占整个动画过程的比例, 浮点型, 0~1 之间
                float fraction = animation.getAnimatedFraction();
                // 直接调用整型估值器, 通过比例计算出宽度,然后再设给 view
                view.getLayoutParams().width = mIntEvaluator.evaluate(fraction, start, end);
                view.requestLayout();
            }
        });

        valueAnimator.setDuration(2000).start();
    }


       六、属性动画的工作原理

                           属性动画要求动画作用的对象提供该属性的 set 方法, 属性动画根据所传递的该属性的初始值和最终值,以动画的效果多次去调用 set 方法。每次给 set 方法的值都不一样,随着时间的推移,所传递的值越来越接近最终值。

下面是属性动画工作原理的基本流程。

【读书笔记】【Android 开发艺术探索】第 7 章 Android 动画深入分析_第1张图片

         七、使用动画的注意的事项

  1、在帧动画中,当图片数量多且图片较大时容易出现 OOM ,在实际的开发中尤其需要注意,尽量避免使用帧动画;

        2、内存泄露

                           在属性动画中有一类无线循环的动画,这类动画需要在 Activity 退出时及时停止,否则将导致 Activity 无法释放从而造成内存泄露。

     private void startObjectAnimation(){
        mAlphaAnimtion = ObjectAnimator.ofFloat(mButton, "alpha",  1f, 0);
        mAlphaAnimtion.setRepeatCount(-1);  // 无限循环
        mAlphaAnimtion.setDuration(3000);
        mAlphaAnimtion.start();
    }

    /**
     *   需要在 Activity  退出时 , 退出属性动画,否则会造成内存泄露
     */
    @Override
    protected void onDestroy() {
        mAlphaAnimtion.cancel();
        super.onDestroy();
    }
3、属性动画在 3.0 已下系统有兼容性问题,在某些特殊场景可能无法正常工作,因此要做好失配工作;

4、View 动画是对 View 的影像做动画,并不是真正地改变 View 状态,因此有时会出现动画完成后 View 无法隐藏的现象。setVisibility(View.GONE) 失效, 这是需要在之前调用 View.clearAnimation() 清除 View 动画。

5、在动画过程中使用 dp ,不要使用 px;

6、使用动画过程中可开启硬件加速,提高动画的流畅性

7、将 view 移动后,在 Android 3.0 之前的系统上,不管是 View 动画还是属性动画,新位置都不会触发单击事件,老位置仍然可以触发点击事件。


                        






         

你可能感兴趣的:(Android,读书笔记)