Android属性动画(Animator)

传统的Animation动画虽然使用方便,但是不适合做具有交互性的动画效果,仅仅适合做展示性的动画,而Animator,属性动画,是改变了该view的内部属性,适合有交互功能的动画。

1.ObjectAnimator

代码:

//第一个参数,动画的对象,第二个参数,需要改变的动画属性,后边参数,变化的具体值(像素)
//若写多个,则动画会同时实行,此例中即旋转360°的同时向x轴和y轴平移
ObjectAnimator.ofFloat(iv, "rotation", 0f, 360f).setDuration(1000).start();
ObjectAnimator.ofFloat(iv, "translationX", 0f, 200f).setDuration(1000).start();
ObjectAnimator.ofFloat(iv, "translationY", 0f, 200f).setDuration(1000).start();

可用的属性有

属性名含义
translationX和translationYx轴和y轴的偏移量
rotation、rotationX和rotationY围绕支点旋转
scaleX和scaleY缩放
pivotX和pivotY控制着view对象的支点位置,围绕该支点旋转缩放处理,默认是view中心点
alpha透明度

2.PropertyValuesHolder

代码:

//效果同上,但动画效果做了优化,而且更有效率
PropertyValuesHolder p1 = PropertyValuesHolder.ofFloat("rotation", 0f, 360f);
PropertyValuesHolder p2 = PropertyValuesHolder.ofFloat("translationX", 0f, 200f);
PropertyValuesHolder p3 = PropertyValuesHolder.ofFloat("translationY", 0f, 200f);
//调用ofPropertyValuesHolder方法传入要动画的控件和PropertyValuesHolder
ObjectAnimator.ofPropertyValuesHolder(iv, p1, p2, p3).setDuration(1000).start();

3.AnimatorSet

提供了更多的动画控制效果

代码:

ObjectAnimator animator1 = ObjectAnimator.ofFloat(iv, "rotation", 0f, 360f);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(iv, "translationX", 0f, 200f);
ObjectAnimator animator3 = ObjectAnimator.ofFloat(iv, "translationY", 0f, 200f);
AnimatorSet set = new AnimatorSet();
set.playTogether(animator1, animator2, animator3);//同时执行
// set.playSequentially(animator1, animator2, animator3); //按顺序执行
set.setDuration(1000);
set.start();

还可以先x轴和y轴同时平移,最后才旋转

代码:

//将set.playTogether()替换为如下代码
set.play(animator2).with(animator3);//with表示同时一起执行
set.play(animator1).after(animator2);//after 2 或者 3 都可以

4.Animator监听事件

代码:

ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(iv, "alpha", 0f, 1f);
objectAnimator.setDuration(1000);
//添加adapter来只监听某个事件或者如下注释的全部监听
objectAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
    super.onAnimationEnd(animation);
    Toast.makeText(getApplicationContext(), "别点了!", Toast.LENGTH_LONG).show();
    }
});
//        objectAnimator.addListener(new Animator.AnimatorListener() {
//            @Override
//            public void onAnimationStart(Animator animation) {
//
//            }
//
//            @Override
//            public void onAnimationEnd(Animator animation) {
//                Toast.makeText(getApplicationContext(), "别点了!", Toast.LENGTH_LONG).show();
//            }
//
//            @Override
//            public void onAnimationCancel(Animator animation) {
//
//            }
//
//            @Override
//            public void onAnimationRepeat(Animator animation) {
//
//            }
//        });
objectAnimator.start();

5.ValueAnimator的使用

ValueAnimator可以当做数值发生器,生产具有一定规律的数字,从而来控制动画的过程。

例如:

代码:按钮的点击事件中,设置ValueAnimator

ValueAnimator animator = ValueAnimator.ofInt(0, 100);
animator.setDuration(5000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        //获取动画过程中的value(上边定义的0~100),用以其他逻辑,例如设置文字
        Integer value = (Integer) animation.getAnimatedValue();
        bt.setText("" + value);
    }
});
animator.start();

6.View的animate方法

View直接就有animate()方法,用以直接驱动简单的属性动画

代码:

bt_time.animate()
        .alpha(0).
        setDuration(1000).
        withStartAction(new Runnable() {
            @Override
            public void run() {
            }
        })
        .withEndAction(new Runnable() {
            @Override
            public void run() {
            }
        })
        .start();

7.ObjectAnimator可改变的属性

属性动画中,可改变的属性,都是对象中对该属性提供了set和get方法的属性,若没有提供set和get方法,则无法正常使用,例如Buttonwidth和height属性,虽然有bt.setWidth()和bt.setHeight()方法,但是源码里即可看到,该方法是改变了Button的最大宽度和高度,并不是改变了该Button的属性,即没有本质上改变,所以使用属性动画改变Buttonwidth和height方法是无效的,即使用语句

// bt = findViewById(R.id.xxx);
ObjectAnimator.ofFloat(bt, "height", 0f, 360f).setDuration(1000).start();

无法达到想要的效果,有两种解决办法。

方法一:使用包装类对要操作的对象属性进行控制,例如

代码:(新建的Wrapper包装类)

public class Wrapper {

    private TextView tv;
    private int width;
    private int height;


    // 对宽和高的真实值进行设置,属性有width和height,必须实现他们的get和set方法
    public int getHeight() {
        return this.tv.getHeight();
    }

    public void setHeight(int height) {
        ViewGroup.LayoutParams params = tv.getLayoutParams();
        params.height = height;
        tv.setLayoutParams(params);
    }

    public int getWidth() {
        return this.tv.getWidth();
    }

    public void setWidth(int width) {
        ViewGroup.LayoutParams params = tv.getLayoutParams();
        params.width = width;
        tv.setLayoutParams(params);
    }

    public Wrapper(TextView tv) {
        this.tv = tv;
    }

}

然后就是使用代码:

// tv = findViewById(R.id.xxx);
Wrapper wrapper = new Wrapper(tv);
// 传入wrapper
ObjectAnimator objectAnimator = ObjectAnimator.ofInt(wrapper, "height", (int) getPX(0), (int) getPX(50));//dp 2 px
objectAnimator.setDuration(2000);
objectAnimator.start();

这样,通过包装类 ,可以真实地控制tv的宽和高

方法二:使用ValueAnimator

ValueAnimator的动画监听函数里可以对控件宽高直接操作

代码:

ValueAnimator animator = ValueAnimator.ofFloat(1, 100);
animator.setDuration(500);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        float value = (float) animation.getAnimatedValue();
        RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) tv.getLayoutParams();
        layoutParams.height = (int) (value * getPX(50));
        tv.setLayoutParams(layoutParams);
    }
});
animator.start();

8.ValueAnimator和TypeEvaluator结合练习

TypeEvaluator即估值器,可以根据动画的进展来估算出要改变的数据大小,进而将这些数据设置给控件的属性从而形成动画。如下代码中。

ValueAnimator.ofObject(evaluator, start, end);

.ofObject(),传入的就是一个继承了TypeEvaluator的估值器。

先实现一个小例子,如下的动画

代码实现:

1.先循环创建出5个TextView,注意动态设置id的时候不要设置成0,否则,若父布局是RelativeLayout时,设置添加规则时,因为在如下语句中

params.addRule(RelativeLayout.BELOW, i);

查看源码如图所示:

第二个参数是规则参照的id,但是为0 的时候,规则无效。

代码:

for (int i = 0; i < 5; i++) {
    TextView tv = new TextView(getApplicationContext());
    tv.setId(i + 1);
    RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,
            RelativeLayout.LayoutParams.MATCH_PARENT);
    params.setMargins(getDP(0), getDP(10), getDP(0), getDP(0));
    params.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
    if (i > 0) {
        params.addRule(RelativeLayout.BELOW, i);
    } else {
        params.addRule(RelativeLayout.BELOW, tv_show.getId());
        params.topMargin = getDP(100);
    }
    tv.setLayoutParams(params);
    tv.getLayoutParams().height = getDP(30);
    tv.getLayoutParams().width = getDP(30);
    tv.setBackgroundResource(R.drawable.circle);
    tv.setOnClickListener(this);
    rl.addView(tv);
}

2.创建自定义的TypeEvaluator,运动轨迹是二次的贝塞尔曲线,midPoint为二次贝塞尔曲线的公式中的P1

这里写图片描述

代码:

public class BallEvaluator implements TypeEvaluator<Point> {

    private Point midPoint;

    public BallEvaluator(Point midPoint) {
        this.midPoint = midPoint;
    }

    @Override
    public Point evaluate(float t, Point startValue, Point endValue) {
        int x = (int) ((1 - t) * (1 - t) * startValue.x + 2 * t * (1 - t) * midPoint.x + t * t * endValue.x);
        int y = (int) ((1 - t) * (1 - t) * startValue.y + 2 * t * (1 - t) * midPoint.y + t * t * endValue.y);
        return new Point(x, y);
    }
}

3.点击响应事件中,获取各个view的位置,并创建ValueAnimator对象,传入TypeEvaluator对象实例

代码:

// 点击事件函数
private void start(final View v) {
    // 创建用以移动的view,位置即是当前view的位置
    final TextView tv = new TextView(getApplicationContext());
    RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,
            RelativeLayout.LayoutParams.MATCH_PARENT);
    tv.setLayoutParams(params);
    tv.getLayoutParams().height = getDP(30);
    tv.getLayoutParams().width = getDP(30);
    tv.setBackgroundResource(R.drawable.circle);
    tv.setX(v.getX());
    tv.setY(v.getY());
    rl.addView(tv); // 加入,记得删除

    // 开始点P0
    int now_x = (int) v.getX();
    int now_y = (int) v.getY();
    Point startposition = new Point(now_x, now_y);

    //中间点P1
    int mid_pointX = (now_x + width) / 2;
    int mid_pointY = now_y - getDP(300); // 向上少许的抛物线
    Point midPoint = new Point(mid_pointX, mid_pointY);

    //结束点P2
    int tv_x = (int) tv_end.getX();
    int tv_y = (int) tv_end.getY();
    Point endposition = new Point(tv_x, tv_y);

    BallEvaluator ballEvaluator = new BallEvaluator(midPoint);
    ValueAnimator animator = ValueAnimator.ofObject(ballEvaluator, startposition, endposition);
    animator.setDuration(400);
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            Point point = (Point) animation.getAnimatedValue();
            tv.setX(point.x);
            tv.setY(point.y);
        }
    });
    animator.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationEnd(Animator animation) {
            super.onAnimationEnd(animation);
            rl.removeView(tv); // 移除创建的view
            tv_end.setText(String.valueOf(++count)); //最后数字+1
        }
    });
    animator.start();
}

效果图:

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值