Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

android animator library

License

NotificationsYou must be signed in to change notification settings

zj565061763/animator

Repository files navigation

对ObjectAnimator和AnimatorSet进行封装,节点动画通过next()和with()方法可以组拼一个动画链,逻辑更清晰。

Gradle

效果

节点动画

节点动画FNodeAnimator几个比较重要的方法解释:

  • with() 返回一个新的节点动画,新的节点动画和上一个节点动画同时执行

  • next() 返回一个新的节点动画,新的节点动画会在上一个节点动画执行完成之后执行

  • node() 返回最后一个节点动画对象

  • chain() 返回整个动画链对象

移动到某个目标View

动画View移动到某个目标View是一个比较复杂的逻辑,包括水平方向和竖直方向;对齐方式,默认是左上角对齐;是否在同一个父容器等。

由于View是可以缩放的,在执行动画之后有可能动画View和目标View都发生了缩放,所以要保证动画执行之后动画View和目标View的位置关系是正确的,需要考虑以下情况之后才可以计算出正确的偏移量:

  • 动画View和目标View的未来缩放值,即动画执行之后的缩放值
  • 动画View和目标View的缩放锚点

如果不希望左上角对齐的话,开发者可以通过PositionShifter接口设置偏移量,具体的使用方法参考下面的方块demo

关于不在同一个父容器的解决方案,参考文末的startAsPop

方块demo

/** * 缩放平移 */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();}

火箭动画demo

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();}

汽车动画demo

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();}

关于startAsPop(boolean clone)

为了解决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);

[8]ページ先頭

©2009-2025 Movatter.jp