Android动画实现包括视图动画以及属性动画。其中比较新颖的自然是5.X系统下的矢量图动画,这个小编也仅仅只是做了初步的了解,毕竟连矢量图的生成还没有掌握好,并且漂亮的矢量图也必须借助工具来实现。本次内容主要是根据“抛物线运动”的个人实现思路来展开描述的,最终实现肯定是各有方案的。
基于以上动画的实现可以实现集合动画,主要是利用AnimationSet.addAnimation();将其组合起来实现。动画执行期间(包括动画的启动,取消,结束以及循环)的监听可以是.setAnimationListener(new Animation.AnimationListener(){};当然,由于我们往往更多关注的是动画结束时候的处理,所以可以直接用.setAnimationListener(new Animation.AnimationListenerAdapter(){};
到这里得理解的一点是,既然通过以上的动画框架接口已实现很好的视图动画效果了,那为什么还要去研究属性动画呢,原因是其动画框架仅能实现显示效果,但无法响应其他事件。故Android3.0之后更多的是用AnimationSet与ObjectAnimator(个体动画属性的更改)配合使用。其中ObjectAnimator常用的动画属性值有
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "translationX", 500);
animator.setDuration(1000);
animator.start();
注意,以上代码实现的是view在x轴方向上移动至距离左上角水平位置为500px的地方,而不是在原基础上向x轴正方向移动500px。如果我们考虑实现多个动画属性能够同时进行,那么可以借助PropertyValueHolder实现,如下代码片段就实现了view在水平偏移的同时进行了垂直方向上的位置偏移。
PropertyValuesHolder pvh1 = PropertyValuesHolder.ofFloat("translationX", 500);
PropertyValuesHolder pvh2 = PropertyValuesHolder.ofFloat("translationY", 500);
ObjectAnimator.ofPropertyValuesHolder(view, pvh1, pvh2).setDuration(1000).start();
在这里如果我们希望动画的集合能够有一定的顺序实现,而不是单一的并发进行。那么除了给每个独立的动画设置动画监听以外我们可以继续用AnimatorSet(注意:这里不是AnimationSet)。其可以通过playogether()、playSequentially()、animSet.Play().with()、befor()、after()分别控制动画的工作方式,从而可好的控制动画的播放顺序。如下实现view同时执行位移,旋转以及缩放的动画效果。其他方法可自行尝试。
ObjectAnimator animator1 = ObjectAnimator.ofFloat(view, "translationX", 500);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(view, "rotationY", 0, 180);
ObjectAnimator animator3 = ObjectAnimator.ofFloat(view, "scaleX", 1.5f);
AnimatorSet set = new AnimatorSet();
set.setDuration(1000);
set.playTogether(animator1, animator2, animator3);
set.start();
除了通过以代码的形式设置属性动画以外,其实我们还习惯通过.xml来实现其一样的效果。如下所示实现视图的透明渐变和引用。
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" android:fillAfter="true" >
<alpha android:duration="1000" android:fromAlpha="0.0" android:toAlpha="1.0" android:interpolator="@android:anim/accelerate_interpolator" />
</set>
Animator anim = AnimatorInflater.loadAnimator(R.drawable.animXml);
anim.setTarget(view);
anim.start;
表示小编基本上没用过布局动画,所谓布局动画就是在ViewGroup添加子View时所呈现出来的一个动画过渡效果。在ViewGroup的.xml当中可以直接设置属性android:animateLayoutChanges=”true”来启动渐显的过渡效果。如果想自定义过渡效果可以类似以下代码实现:
Animator anim = AnimatorInflater.loadAnimator(R.drawable.animXml);// 定义动画内容
LayoutAnimationController lac = new LayoutAnimationController(anim );
/* * LayoutAnimationController.ORDER_RANDOM 随机 * LayoutAnimationController.ORDER_NORMAL 顺序 * LayoutAnimationController.ORDER_REVERSE 反序 * / lac.setOrder(LayoutAnimationController.ORDER_RANDOM); lac.setDelay(0.5f); view.setLayoutAnimation(lac);
对于插值器,不得不说这才是个狠角色。因为小编的抛物线运动轨迹就是通过插值器的速率变换来实现的。可能有些人还不了解插值器是什么,其实最常见的插值器就是我们在定义动画时,所引用到的加速减速以及弹回等改变动画运行状态的工具。而Interpolators主要包含以下几种插值器。
Interpolator对象 | 资源ID | 功能作用 |
---|---|---|
AccelerateDecelerateInterpolator | @android:anim/accelerate_decelerate_interpolator | 先加速再减速 |
AccelerateInterpolator | @android:anim/accelerate_interpolator | 加速 |
AnticipateInterpolator | @android:anim/anticipate_interpolator | 先回退一小步然后加速前进 |
AnticipateOvershootInterpolator | @android:anim/anticipate_overshoot_interpolator | 在上一个基础上超出终点一小步再回到终点 |
BounceInterpolator | @android:anim/bounce_interpolator | 最后阶段弹球效果 |
CycleInterpolator | @android:anim/cycle_interpolator | 周期运动 |
DecelerateInterpolator | @android:anim/decelerate_interpolator | 减速 |
LinearInterpolator | @android:anim/linear_interpolator | 匀速 |
OvershootInterpolator | @android:anim/overshoot_interpolator | 快速到达终点并超出一小步最后回到终点 |
而每一种插值器自然而然的也都是继承自Interpolators实现,并重写其核心方法public float getInterpolation(float input){};该函数参数input值为插值器x轴方向上0.0f到1.0f的变换过程。对应的返回值即代表当前的动画运行速率。根据这一点,我们只要友好地编上一条自定义速率变换方程就可以轻松地实现类似抛物线的动画了。当然,这种实现比较抽象,前提是必须先理解速率变换给动画运行所带来的影响,比如说当速率是负数的时候,会改变动画的运动方向。下面给到小编自己通过AnticipateInterpolator来实现view呈类似抛物线运动的效果及代码。
为了方便理解,在了解代码具体实现之前请先了解相关AnticipateInterpolator速率变换轨迹如下。具体可参照 http://my.oschina.net/banxi/blog/135633?fromerr=uv67kzf9#OSC_h2_7 (包括其他插值器对应的速率轨迹图详细注解)。
@OnClick({R.id.reddot_btn_parabola})
private void mOnClick(View view) {
switch (view.getId()) {
case R.id.reddot_btn_parabola:
// 每点击一次按钮就给父容器mMainRly(RelativeLayout)添加一个小球
final ImageView img = getClickDot(view, 30);
mMainRly.addView(img);
// 获取底下购物车的位置
final PointF end = getViewPointF(mShopCartImg);
ObjectAnimator animator1 = ObjectAnimator.ofFloat(img, "x", end.x);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(img, "y", end.y);
animator2.setInterpolator(new AnticipateInterpolator(3.0f));
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(800);
animatorSet.playTogether(animator1, animator2);
animatorSet.start();
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// 小球运行结束后从父容器中移除
mMainRly.removeView(img);
}
});
break;
}
}
/** * @param v 被点击的视图(用于采集球的位置) * @param size 球体大小 * @return 根据位置返回新的球 */
private ImageView getClickDot(View v, int size) {
ImageView dotIv = new ImageView(this);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(size, size);
dotIv.setLayoutParams(params);
dotIv.setX(v.getX() + v.getWidth() / 2 - size / 2);
dotIv.setY(v.getY() + v.getHeight() / 2 - size / 2);
dotIv.setImageResource(R.drawable.vector_reddot_small);
return dotIv;
}
/** * @param view 取点视图 * @return 获取视图坐标点 */
private PointF getViewPointF(View view) {
PointF pointF = new PointF();
pointF.x = view.getX() + view.getWidth() / 2;
pointF.y = view.getY() + view.getHeight() / 2;
return pointF;
}
自定义动画效果继承自Animation,需要重写initialize();以及applyTransformation();两个方法,其中initialize();应用于动画的一些属性初始化,而applyTransformation();才是自定义动画的核心过程。具体的实现案例“3D翻转”效果可参考http://chroya.iteye.com/blog/828094