viewpageAndroid知识Android开发

Android 动画基础知识学习(下)

2016-08-26  本文已影响633人  英勇青铜5

学习资料:Android开发艺术探索Animation的api

1.属性动画

属性动画可以对任意对象的属性进行动画不仅仅是View,动画默认时间间隔是300ms,默认帧率是100ms/帧

作用:在一个时间间隔内完成对一个对象从属性值到另一个属性值的改变。

三个常用类:ValueAnimator,ObjectAnimator,AnimatorSet


属性动画

Java代码

private void initView() {
        Button bt = (Button) findViewById(R.id.bt_object_animation_activity);
        ImageView iv = (ImageView) findViewById(R.id.iv_object_animation_activity);

        //改变背景属性
        ValueAnimator colorAnim = ObjectAnimator.ofInt(iv, "backgroundColor", Color.parseColor("#FF4081"), Color.CYAN);
        colorAnim.setRepeatCount(2);
        colorAnim.setRepeatMode(ObjectAnimator.REVERSE);
        colorAnim.setDuration(1000);
        colorAnim.setEvaluator(new ArgbEvaluator());//估值器

        //动画集合
        AnimatorSet set = new AnimatorSet();
        set.playTogether(
                ObjectAnimator.ofFloat(iv, "rotationX", 0, 360),//绕x轴旋转360度
                ObjectAnimator.ofFloat(iv, "rotation", 0, -90),//逆时针旋转90度
                ObjectAnimator.ofFloat(iv, "translationX", 0, 90),//右移
                ObjectAnimator.ofFloat(iv, "scaleY", 1, 0.5f),//y轴缩放到一半
                ObjectAnimator.ofFloat(iv, "alpha", 1, 0.25f, 1)//透明度变换
        );
        //延迟一秒开始
        set.setStartDelay(1000);

        bt.setOnClickListener((v) -> {
            //改变属性 位置  向下移动iv高的二分之一
            ObjectAnimator.ofFloat(iv, "translationY", iv.getHeight() / 2).start();
            //背景属性改变开始
            colorAnim.start();
            //集合动画
            set.setDuration(3000).start();
        });
}

轴点默认为View的中心点。

常用的propertyName


1.2 常用方法介绍

Animation的api

1.2.1 ObjectAnimator

Constructs and returns an ObjectAnimator that animates between float values.

返回一个根据方法内的具体的values创建的ObjectAnimator对象

  1. Object target ,目标控件View的对象
  2. String propertyName,属性的名字
  3. float... values ,根据具体需求需要的属性值

Constructs and returns an ObjectAnimator that animates between the sets of values specified in PropertyValueHolder objects

返回一个由PropertyValueHolder对象创建的ObjectAnimator对象

 public void byPropertyValuesHolder(ImageView iv) {
       PropertyValuesHolder pvh_1 = PropertyValuesHolder.ofFloat("rotationX", 0, 360);
       PropertyValuesHolder pvh_2 = PropertyValuesHolder.ofFloat("rotation", 0, -90);
       PropertyValuesHolder pvh_3 = PropertyValuesHolder.ofFloat("alpha", 1, 0.25f, 1);
       ObjectAnimator.ofPropertyValuesHolder(iv, pvh_1, pvh_2, pvh_3).setDuration(1000).start();
}

Constructs and returns an ObjectAnimator that animates between int values.

返回一个由int值属性创建的ObjectAnimator对象



1.2.2 ValueAnimator

Adds a listener to the set of listeners that are sent update events through the life of an animation.

添加一个监听,可以用来在动画过程中改变属性




1.2.3 AnimatorSet

Sets up this AnimatorSet to play all of the supplied animations at the same time.

同时播放所有的Animator动画对象。


Sets up this AnimatorSet to play each of the supplied animations when the previous animation ends.

顺序播放Animator动画对象

Sets the TimeInterpolator for all current child animations of this AnimatorSet.

设置插值器


1.2.4 Animator

直接子类:AnimatorSetValueAnimator
间接子类:ObjectAnimatorTimeAnimator

Animator类的方法子类都可以直接使用。

Adds a listener to the set of listeners that are sent events through the life of an animation, such as start, repeat, and end.

为动画添加一个监听过程的接口。如果不想实现AnimatorListener接口中的所有方法也可以继承AnimatorListenerAdapter

set.addListener(new Animator.AnimatorListener() {
    @Override
    public void onAnimationStart(Animator animator) {
         toast("动画开始");
    }
    @Override
    public void onAnimationEnd(Animator animator) {
         toast("动画结束");
    }
    @Override
    public void onAnimationCancel(Animator animator) {
         toast("动画取消");
    }
    @Override
    public void onAnimationRepeat(Animator animator) {
        toast("动画重建");
    }
});

实现了接口中全部的方法。


继承AnimatorListenerAdapter:

set.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        super.onAnimationEnd(animation);
        toast("动画结束");
    }
});

根据实际需求,实现不同的方法。


Adds a pause listener to this animator

为动画增加暂停监听


2.插值器和估值器

TimeInterpolator,时间插值器。用来根据时间流逝的百分比来计算出当前属性的值变化的百分比。

常用插值器:

名称 作用
LinearInterpolator 线性插值器。匀速动画
AccelerateDecelerateInterpolator 加速减速插值器
DecelerateInterpolator 匀减速插值器。动作越来越慢
BounceInterpolator 回弹插值器。到达平移后,回弹
CycleInterpolator 循环插值器。在两点间往还运动
PathInterpolator 路径插值器。根据单一方向定义的路径坐标运动
OvershootInterpolator 超越插值器。超出后,再返回来
AnticipateInterpolator 预期插值器。先反向运动再根据指定的方向运动

都是Interpolator的子类


TypeEvaluator,类型估值算法(估值器)。用来根据当前属性变化改变的百分比来计算改变后的属性值。


2.1简单Demo

模拟小球下落
private void initView() {
    Button bt = (Button) findViewById(R.id.bt_interpolator_activity);
    ImageView iv = (ImageView) findViewById(R.id.iv_interpolator_activity);
    LinearLayout root = (LinearLayout) findViewById(R.id.root_interpolator_activity);//根布局

    AnimatorSet set = new AnimatorSet();
    set.setInterpolator(new BounceInterpolator());
    set.setDuration(3000);
    //利用View的post方法拿到根布局的高度
    root.post(() -> {
        //计算下降高度
        int height = root.getHeight() - iv.getHeight() - bt.getHeight();
        //设置动画
        set.play(ObjectAnimator.ofFloat(iv, "translationY", height));
    });
        
    bt.setOnClickListener(v ->set.start());
    }

利用BounceInterpolator可以很方便的做出模拟小球下落的动画。也可以根据需求进行自定义插值器。


2.2 对任意属性做动画

Object的属性abc做动画,必须满足2个条件:

  1. object必须提供setAbc()的方法。如果动画的时候没有传递初始值,还要提供getAbc()方法。因为系统要去abc的初始值。如果不满足,程序直接Crash
  2. objectsetAbc对属性abc所做的改变必须能够通过某种方法反映出来,比如UI改变之类的。这条不满足,动画无效但程序不会Crash

2.2.1 改变Button的宽度

例如,想要利用属性对话来改变一个Button的宽度。

private void initView() {
    Button bt = (Button) findViewById(R.id.bt_button_activity);
    bt.setOnClickListener((v) -> performAnimate(bt));
}

private void performAnimate(Button bt) {
    ObjectAnimator.ofInt(bt, "width", 500).setDuration(1000).start();
}

实际测试,这段代码完全不起作用。


2.2.2不起作用的原因

Button继承的TextView

/**
 * Makes the TextView exactly this many pixels wide.
 * You could do the same thing by specifying this number in the
 * LayoutParams.
 *
 * @see #setMaxWidth(int)
 * @see #setMinWidth(int)
 * @see #getMinWidth()
 * @see #getMaxWidth()
 *
 * @attr ref android.R.styleable#TextView_width
 */
 @android.view.RemotableViewMethod
 public void setWidth(int pixels) {
    mMaxWidth = mMinWidth = pixels;
    mMaxWidthMode = mMinWidthMode = PIXELS;

    requestLayout();
    invalidate();
 }


 /**
  * Return the width of the your view.
  *
  * @return The width of your view, in pixels.
  */
  @ViewDebug.ExportedProperty(category = "layout")
  public final int getWidth() {
     return mRight - mLeft;
  }

getWidth()方法View的方法,是可以获取Button高度的。setWidth()方法TextView和子类的专属方法。是用来设置TextView的最大宽度和最小宽度的,并不是用来设置TextView的宽度的方法。TextView的宽度对应于XMLandroid:layout_widthsetWidth方法对应的是android:width

也就是说:ButtonsetWidth()getWidth()对应的就不是一个属性。只满足的条件1,不满足条件2


2.2.3 解决办法

有三种解决办法:


getset方法往往拿不到权限。

利用包装类方法:

private void initView() {
    Button bt = (Button) findViewById(R.id.bt_button_activity);
    bt.setOnClickListener((v) -> performAnimate(bt,bt.getWidth()));
}
    
 private void performAnimate(Button bt,int width) {
    ButtonWrapper wrapper = new ButtonWrapper(bt);
    wrapper.setWidth(width);
    ObjectAnimator.ofInt(wrapper, "width", 500).setDuration(1000).start();
}

private static class ButtonWrapper {
    private View target;

    public ButtonWrapper(View target) {
        this.target = target;
    }

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

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

拿到Button的宽度后,设置给ButtonWrapper。这样动画开始后,Button会从原始大小开始变化。


利用ValueAnimation方法:

private void initView() {
    Button bt = (Button) findViewById(R.id.bt_button_activity);
       
    bt.setOnClickListener((v) -> performAnimate(bt,bt.getWidth(),500));
}
    
private void performAnimate(Button bt,int start, int end) {
    ValueAnimator valueAnimator = ValueAnimator.ofInt(1,100);

    //IntEvaluator对象,估值的时候使用
    IntEvaluator intEvaluator = new IntEvaluator();

    valueAnimator.addUpdateListener((animator -> {
        //获取当前动画的进度值 , 整型, 1到100
        int currentValue = (int) animator.getAnimatedValue();

        //获取当前进度的占整个动画过程的比例,浮点型, 0到1
        float fraction = animator.getAnimatedFraction();
        //直接利用整型估值器,通过比例计算宽度,然后Button设置
        bt.getLayoutParams().width = intEvaluator.evaluate(fraction,start,end);
        bt.requestLayout();
    }));
    //开启动画
    valueAnimator.setDuration(1000).start();
}
 

监控动画过程,利用IntEvaluator估值器


3.最后

动画基础知识大概介绍完,自定义TypeEvaluatorInterpolator以及配合自定义View更多高级的用法,以后再做补充。

接下来相当长的时间会用来学习自定义View。想通过一系列博客来记录学习,一开始先学习一些View的基础属性知识为学习自定义View做准备,再学习具体的测量,绘制过程,View的事件体系,工作原理。学习过程中间会加入继续对动画的深入学习,也可能会加入RxJava的后续学习或者其他的框架的学习。

上一篇 下一篇

猜你喜欢

热点阅读