- Notifications
You must be signed in to change notification settings - Fork7
zj565061763/animator
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
对ObjectAnimator和AnimatorSet进行封装,节点动画通过next()和with()方法可以组拼一个动画链,逻辑更清晰。
节点动画FNodeAnimator几个比较重要的方法解释:
with() 返回一个新的节点动画,新的节点动画和上一个节点动画同时执行
next() 返回一个新的节点动画,新的节点动画会在上一个节点动画执行完成之后执行
node() 返回最后一个节点动画对象
chain() 返回整个动画链对象
动画View移动到某个目标View是一个比较复杂的逻辑,包括水平方向和竖直方向;对齐方式,默认是左上角对齐;是否在同一个父容器等。
由于View是可以缩放的,在执行动画之后有可能动画View和目标View都发生了缩放,所以要保证动画执行之后动画View和目标View的位置关系是正确的,需要考虑以下情况之后才可以计算出正确的偏移量:
- 动画View和目标View的未来缩放值,即动画执行之后的缩放值
- 动画View和目标View的缩放锚点
如果不希望左上角对齐的话,开发者可以通过PositionShifter接口设置偏移量,具体的使用方法参考下面的方块demo
关于不在同一个父容器的解决方案,参考文末的startAsPop
/** * 缩放平移 */privatevoidscale(){newFNodeAnimator(fl_video) .scaleXToView(fl_video_target)// 缩放到目标view的x .with().scaleYToView(fl_video_target)// 缩放到目标view的y .with().moveXToView()// 返回一个配置对象,可以配置移动到目标view的x所需要的参数 .newTarget(fl_video_target).setFutureScale(fl_video_target)// 添加一个移动目标view,并设置目标view未来x的缩放目标 .node()// 返回node节点 .with().moveYToView()// 返回一个配置对象,可以配置移动到目标view的y所需要的参数 .newTarget(fl_video_target).setFutureScale(fl_video_target)// 添加一个移动目标view,并设置目标view未来y的缩放目标 .node()// 返回node节点 .chain().start();// 开始整个链条动画}/** * 平移 */privatevoidmove(){newFNodeAnimator(fl_video) .moveXToView() .newTarget(fl_video_target_1).setPositionShifter(newAlignCenterPositionShifter())// 配置位置转移器,设置动画View和目标View水平方向中心对齐 .node() .with().moveYToView() .newTarget(fl_video_target_1).setPositionShifter(newAlignCenterPositionShifter())// 配置位置转移器,设置动画View和目标View竖直方向中心对齐 .node() .chain().start();}
publicvoidonclickStart(Viewv){if (mAnimatorChain !=null &&mAnimatorChain.isRunning()) {return; }/** * true-调试模式,会输出整个动画链的结构,方便开发调试,可以给每个节点动画设置描述 * * 过滤tag:AnimatorChain,demo中的日志输入如下: * * Head:(火箭淡入 alpha:500) * Next:(延迟500毫秒 null:500) * Next:(开始数字缩放X scaleX:1000) With:(开始数字缩放Y scaleY:1000) * Next:(火箭起飞 translationY:3000) With:(烟雾淡入 alpha:3000 startDelay:500) * Next:(烟雾淡出 alpha:500) * */mAnimatorChain =newFNodeAnimator(fl_rocket_root).chain().setDebug(true);mAnimatorChain.currentNode() .alpha(0,1f).setDuration(500).setDesc("火箭淡入") .next().setDuration(500).setDesc("延迟500毫秒") .next().setTarget(tv_number).scaleX(1f,0f).setRepeatCount(2).setDuration(1000).setDesc("开始数字缩放X") .withClone().scaleY(1f,0f).setDesc("开始数字缩放Y") .addListener(newFAnimatorListener() {@OverridepublicvoidonAnimationStart(Animatoranimation) {super.onAnimationStart(animation);tv_number.setText(String.valueOf(mNumber)); }@OverridepublicvoidonAnimationEnd(Animatoranimation) {super.onAnimationEnd(animation);mNumber =3;tv_number.setVisibility(View.INVISIBLE); }@OverridepublicvoidonAnimationRepeat(Animatoranimation) {super.onAnimationRepeat(animation);mNumber--;tv_number.setText(String.valueOf(mNumber)); } }) .next().setTarget(fl_rocket_root).translationY(0, -getResources().getDisplayMetrics().heightPixels).setDesc("火箭起飞") .setDuration(3000).setInterpolator(newAccelerateInterpolator()) .addListener(newOnEndInvisible(),newOnEndReset())//动画结束隐藏,重置fl_rocket_root .addListener(newFAnimatorListener() {@OverridepublicvoidonAnimationStart(Animatoranimation) {super.onAnimationStart(animation);AnimationDrawableanimationDrawable = (AnimationDrawable)iv_rocket.getDrawable();animationDrawable.start();//开始火箭喷火动画 }@OverridepublicvoidonAnimationEnd(Animatoranimation) {super.onAnimationEnd(animation);AnimationDrawableanimationDrawable = (AnimationDrawable)iv_rocket.getDrawable();animationDrawable.stop();//停止火箭喷火动画 } }) .with().setTarget(iv_rocket_smoke).alpha(0,1f).setDuration(3000).setStartDelay(500).setDesc("烟雾淡入") .next().alpha(1f,0).setDuration(500).setDesc("烟雾淡出") .addListener(newOnEndInvisible())//动画结束隐藏烟雾 .chain().start();}
publicvoidonclickStart(Viewv){if (mAnimatorChain !=null) {if (!mAnimatorChain.isRunning())mAnimatorChain.start();return; }//汽车下来intcarDownX1 =getScreenWidth();intcarDownX2 =getScreenWidth() /2 -fl_down_car.getWidth() /2;intcarDownX3 = -fl_down_car.getWidth();intcarDownY1 = -fl_down_car.getHeight();intcarDownY2 =getScreenHeight() /2 -fl_down_car.getHeight() /2;intcarDownY3 =getScreenHeight();//汽车上去intcarUpX1 = -fl_down_car.getWidth();intcarUpX2 =getScreenWidth() /2 -fl_down_car.getWidth() /2;intcarUpX3 =getScreenWidth();intcarUpY1 =getScreenHeight();intcarUpY2 =getScreenHeight() /2 -fl_down_car.getHeight() /2;intcarUpY3 = -fl_down_car.getHeight();mAnimatorChain =newFNodeAnimator(iv_down_car_front_tyre).chain().setDebug(true);mAnimatorChain.currentNode() .rotation(-360).setRepeatCount(-1).setDuration(1000) .addListener(newOnEndReset(),newOnEndInvisible()).setDesc("下-前轮旋转") .withClone().setTarget(iv_down_car_back_tyre) .addListener(newOnEndReset(),newOnEndInvisible()).setDesc("下-后轮旋转") .with().setTarget(fl_down_car).moveToX(carDownX1,carDownX2) .setDuration(1500).setInterpolator(newDecelerateInterpolator()).setDesc("X右上角移动到屏幕中央") .withClone().moveToY(carDownY1,carDownY2).setDesc("Y右上角移动到屏幕中央") .next().setDuration(500).setDesc("屏幕中央停止500毫秒") .next().moveToX(carDownX2,carDownX3) .setDuration(1500).setInterpolator(newAccelerateInterpolator()).setDesc("X屏幕中央移动到左下角") .withClone().moveToY(carDownY2,carDownY3) .addListener(newOnEndInvisible(),newOnEndReset()).setDesc("Y屏幕中央移动到左下角") .next().setTarget(iv_up_car_front_tyre).rotation(360).setRepeatCount(-1).setDuration(1000) .addListener(newOnEndReset(),newOnEndInvisible()).setDesc("上-前轮旋转") .withClone().setTarget(iv_up_car_back_tyre) .addListener(newOnEndReset(),newOnEndInvisible()).setDesc("上-后轮旋转") .with().setTarget(fl_up_car).moveToX(carUpX1,carUpX2) .setDuration(1500).setInterpolator(newDecelerateInterpolator()).setDesc("X左下角移动到屏幕中央") .withClone().moveToY(carUpY1,carUpY2).setDesc("Y左下角移动到屏幕中央") .next().setDuration(500).setDesc("屏幕中央停止500毫秒") .next().moveToX(carUpX2,carUpX3) .setDuration(1500).setInterpolator(newAccelerateInterpolator()).setDesc("X屏幕中央移动到右上角") .withClone().moveToY(carUpY2,carUpY3).setDesc("Y屏幕中央移动到右上角") .addListener(newOnEndInvisible(fl_up_car),newOnEndReset(fl_up_car),newFAnimatorListener() {@OverridepublicvoidonAnimationEnd(Animatoranimation) {super.onAnimationEnd(animation);// 最后一个动画执行完成后,停止动画链,要不然轮子无限旋转,判断的时候动画链一直处于运行中mAnimatorChain.cancel(); } }) .chain().start();}
为了解决view没办法超出父布局边界来执行动画的问题,提供了这个方法
/** * 实现原理: <br> * <p> * 1.对target截图然后设置给ImageView <br> * 2.把ImageView添加到Activity中android.R.id.content的FrameLayout里面 <br> * 注意:这里的Activity对象是从原target获取,所以要保证原target的getContext()返回的是Activity对象,否则会失败 <br> * 3.根据传入的参数是否克隆,来决定把ImageView设置给哪个动画对象执行 * <p> * 参数说明: <br> * clone == true,执行克隆的对象,返回克隆的对象 <br> * clone == false,执行当前对象,返回当前对象 <br> * <br> * 注意:不克隆的性能会好一点,但是会修改当前动画对象的target,开发者可以根据具体的应用场景来决定是否克隆 * * @param clone * @return 如果返回不为null,表示返回的是克隆对象或者当前对象,取决于传入的参数;如果返回null,表示执行失败 */TstartAsPop(booleanclone);
About
android animator library
Topics
Resources
License
Stars
Watchers
Forks
Packages0
No packages published