Android动画随笔

107 阅读4分钟

主要内容包括两部分,首先是视图动画(也叫补间动画),其次是属性动画

属性动画优于视图动画的地方在于,视图动画只是改变动画的显示,而并非真正响应事件,而属性动画中包括主要的两部分,其一为ObjectAnimator,其二ValueAnimator,值得一提的是,ObjectAnimator继承于ValueAnimator,并且更加耀眼。如果你想要出发点击事件,然后进行动画,则需要选择属性动画。

对于ObjectAnimator而言,操作的属性需要具有get和set方法,至于一些没有get和set方法的属性,例如width,可以使用包装类的方式进行封装,具体展示如下demo。

对于ofInt或者ofFloat而言,之前有一种疑问,为什么这里总是以数值的形式表示,现在分析一下,认为关键在于属性值。对于这类动画的变化,实际是对属性值得变化,因此设置了该两种方法。

并且,我们经常在回调监听得过程中使用到animation.getAnimatedValue(),该方法获取的是完成度,实际上是一种百分制,而并非上述ofInt或者ofFloat中设置的数值变化。

在常见的ofFloat参数中,往往具有以下两种

第一种:PropertyValuesHolder propertyValuesHolder = PropertyValuesHolder.ofFloat("translationX", 300f);
第二种:PropertyValuesHolder propertyValuesHolder1 = PropertyValuesHolder.ofFloat("scaleX", 1f, 0, 1f);

至于value位置的参数个数,可以看作是一种变化,即平移到300f的位置,或者是将X坐标从100%缩放到0然后再变回100%。

对于ValueAnimator而言,可以直接进行值的变换,但是相对于ObjectAnimator而言,还需要进一步手动去获取变化的值,并将其应用到部件的属性中,例如demo中展示的。

值得一提的是,lz在demo实验过程中遇到了一个问题,即在onCreate中无法获取到mButton控件的高度,查阅资料后发现,是由于View的空间是在onLayer后才能够获取到宽高,而View和Activity的生命周期并非一一绑定的,并且,在onResume中也无法获得,具体原因可以简单解释为View的绘制和布置执行完的时机在这些的后面。

如果想要在onCreate中或者onResume中获取得到控件的宽高,可以采用post的方式,在消息队列末尾加入一则任务,例如demo中展示的。

至于好多文章中提到的动画插值器,简单理解为变换加速度,使用起来也很简单,分为两种,一种是xml中直接加入该属性,另一种是在代码文件中new一类插值器,例如demo中展示的。

DEMO:

```

import androidx.appcompat.app.AppCompatActivity;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.AnimationUtils;
import android.view.animation.TranslateAnimation;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {


    private static final String TAG = "VALUEANIMATOR_LOG";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        AnimView animView = findViewById(R.id.animView);
        Button mButton = findViewById(R.id.button);
        //wrapperAnim(mButton);
        //translationXAnim(mButton);
        //xmlAnim(animView);
        //alphaAnim(animView);
        //translateAnim(animView);
        //setAnim(mButton);
        valueAnim(mButton);

    }

    private void valueAnim(Button button) {
        ValueAnimator animator = ValueAnimator.ofFloat(0, 400);
        animator.setTarget(button);
        animator.setDuration(2000).start();
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                Log.d(TAG, "valueAnim: the button`s height is" + button.getHeight());
            }
        });

        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                Float value = (Float) animation.getAnimatedValue();
                Log.d(TAG, "onAnimationUpdate: " + value);
                button.setWidth(value.intValue());
            }
        });

    }

    private void setAnim(Button Button) {
        PropertyValuesHolder propertyValuesHolder = PropertyValuesHolder.ofFloat("translationX", 300f);
        PropertyValuesHolder propertyValuesHolder1 = PropertyValuesHolder.ofFloat("scaleX", 1f, 0, 1f);
        PropertyValuesHolder propertyValuesHolder2 = PropertyValuesHolder.ofFloat("scaleY", 1f, 0, 1f);
        ObjectAnimator.ofPropertyValuesHolder(Button, propertyValuesHolder, propertyValuesHolder1, propertyValuesHolder2).setDuration(2000).start();
    }

    private void wrapperAnim(Button button) {
        WrapperView wrapperView = new WrapperView(button);
        ObjectAnimator objectAnimator = ObjectAnimator.ofInt(wrapperView, "width", 500);
        //两种设置监听的方式,首先是new Animator.AnimatorListener(),需要对四个方法全部实现,然后是new AnimatorListenerAdapter()
        //这个方法帮助我们空实现了所有的方法,所以我们只需要实现我们用得到的方法进行监听即可
/*        objectAnimator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationEnd(Animator animation) {

            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });*/
        objectAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                Toast.makeText(MainActivity.this,"this", Toast.LENGTH_SHORT).show();

            }
        });
        objectAnimator.setDuration(5000);
        objectAnimator.start();
    }

    private void translationXAnim(Button button) {
        ObjectAnimator animator = ObjectAnimator.ofFloat(button, "translationX", 300);
        animator.setDuration(3000);
        animator.start();
    }

    private void setAnim(){
        AnimationSet animationSet = new AnimationSet(true);
        animationSet.setDuration(5000);
        animationSet.addAnimation(new AlphaAnimation(0,1));
    }

    private void alphaAnim(AnimView animView) {
        AlphaAnimation alphaAnimation = new AlphaAnimation(0,1);
        alphaAnimation.setDuration(5000);
        animView.startAnimation(alphaAnimation);
        alphaAnimation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                //动画开始的时候调用
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                //动画结束的时候调用
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
                //动画重新执行的时候调用
            }
        });
    }

    private void translateAnim(AnimView animView){
        TranslateAnimation animation = new TranslateAnimation(0, 100, 0, 200);
        animation.setDuration(5000);
        animView.startAnimation(animation);
    }

    private void xmlAnim(AnimView animView) {
        //使用AnimationUtils类的静态方法loadAnimation方法可以达到加载XML文件中的动画的目的
        Animation animation = AnimationUtils.loadAnimation(this, R.anim.scaleanim);
        animView.setAnimation(animation);
    }

    //包装类,可以拓展其他的属性,只要赋予其get和set方法就可以实现类似于translationX的形式。
    private static class WrapperView{

        private View mTarget;

        public WrapperView() {
        }

        public WrapperView(View target) {
            mTarget = target;
        }

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

        public void setWidth(int width){
            mTarget.getLayoutParams().width = width;
            mTarget.requestLayout();
        }
    }
}
```

未完待续 如有不对的地方请指出,不完善的在后续学习中会继续补充