高仿马蜂窝旅游头像泡泡动画
当pm制定完下一版本需求打开马蜂窝旅游app准备出去嗨一圈的时候 看到了马蜂窝旅游app的一个客户头像动画后。。。(=@__@=) 先看看效果图
demo_01
效果分析:
demo_03
- 涉及到有多个view在做动画操作 这里需要继承FrameLayout来左父布局 供图片做动画操作
- 每个子view的动画路径相似于S型 我这里采用的是三阶贝塞尔曲线和PathMeasure来完成动画运动路径的封装
- 每个子view动画执行完后 是移除增加新的view进来 还是回收重新利用 本案例是直接移除再增加新的(回收重新利用还没来得及去考虑该怎样写)
- 动画是循环不停的播放 我采用的是RxJava timer()操作符 不断的发送随机推迟消息去通知动画的执行
- 最后就剩下少量中止动画操作的开关设定
实现步骤
1.少量基本的初始化工作
public class HeadBubbleView extends FrameLayout { //这个position很重要 不断的取出图片资源 靠它累加完成的 private int position = 0; public HeadBubbleView(@NonNull Context context) { this(context,null); } public HeadBubbleView(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; setFocusable(false); //三阶贝塞尔曲线控制点一 controlPointOne = new Point(); //三阶贝塞尔曲线控制点二 controlPointTwo = new Point(); //每个子view的宽高是固定的 viewWidth = viewHeight = SizeUtils.dp2px(context, 22); marginLeft = SizeUtils.dp2px(context, 15); marginBot = SizeUtils.dp2px(context, 21); //父View的高度也是固定的 height = SizeUtils.dp2px(context, 130); //用于从PathMeasure 中不断的取出 曲线的路径值 pos = new float[2]; tan = new float[2]; initView(); }2.初始化的时候数据的加载状态
private void initView() { //这个ImageView将不执行动画 用于底部不断切换图片展现 tempImageView = getImageView(); textView = getTextView(); initData(tempImageView); } //创立执行动画的具体角色 private ImageView getImageView() { LayoutParams layoutParams = new LayoutParams(viewWidth, viewHeight); ImageView roundedImageView = new ImageView(getContext()); roundedImageView.setScaleType(ImageView.ScaleType.FIT_XY); layoutParams.gravity = Gravity.BOTTOM | Gravity.END; layoutParams.setMargins(0, 0, marginLeft, marginBot); addView(roundedImageView, layoutParams); return roundedImageView; }//创立用于显示坐标xx来过的TextView private TextView getTextView() { int bottom = SizeUtils.dp2px(mContext, 23); int right = SizeUtils.dp2px(mContext, 41); LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); layoutParams.gravity = Gravity.END | Gravity.BOTTOM; layoutParams.setMargins(0, 0, right, bottom); TextView tv_name = new TextView(mContext); tv_name.setTextSize(12); tv_name.setTextColor(Color.WHITE); addView(tv_name, layoutParams); return tv_name; }//第一次加载数据private void initData(ImageView roundedImageView) { if (null != browseEntities && browseEntities.size() > 0) { //第一次去第0个数据 BrowseEntity browseEntity = browseEntities.get(position); if (null != browseEntity) { roundedImageView.setBackgroundResource(browseEntity.drawableId); String username = browseEntity.name; if (!TextUtils.isEmpty(username)) { textView.setText(username + "来过"); } } } }由上面的操作就完成基础显示
demo_02
3.接下来完成第一阶段动画 由最小缩放到最大
private boolean createAnimView() { if (!isStop) { return true; } ImageView imageView = getImageView(); //创立好后 设置缩放到最小 imageView.setScaleX(0); imageView.setScaleY(0); initData(imageView); startScaleAnim(imageView); return false; }//执行缩放动画 private void startScaleAnim(final ImageView imageView) { ValueAnimator valueAnimator = ValueAnimator.ofFloat(0.0f, 1.0f); valueAnimator.setDuration(800); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float animatedValue = (float) animation.getAnimatedValue(); imageView.setScaleX(0.1f + animatedValue); imageView.setScaleY(0.1f + animatedValue); } }); valueAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (position == browseEntities.size() - 1) { position = 0; } else { position++; } BrowseEntity browseEntity = browseEntities.get(position); //动画执行完后要立马取出下一个图片 把底部的图片显示升级 tempImageView.setBackgroundResource(browseEntity.drawableId); //动画执行完执行平移动画 startTranslationAnimator(imageView); } }); valueAnimator.start(); }
demo_04
4.第二阶段的曲线运动缩小动画
private void startTranslationAnimator(final ImageView imageView) { Path path; int seed = (int) (Math.random() * 100); //根据随机数来确定是走左边曲线还是右边曲线 if (seed % 2 == 0) { //曲线路径的封装 path = createRightPath(); } else { //曲线路径的封装 path = createLeftPath(); } //通过PathMeasure 和ValueAnimator结合 在不同的阶段取出运动路径的x,y值 final PathMeasure pathMeasure = new PathMeasure(path, false); final ValueAnimator valueAnimator = ValueAnimator.ofFloat(1.0f, 0.0f); valueAnimator.setDuration(riseDuration); valueAnimator.setInterpolator(new LinearInterpolator()); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float animatedValue = (float) animation.getAnimatedValue(); int length = (int) (pathMeasure.getLength() * animatedValue); //在不同的阶段取出运动路径的x,y值 pathMeasure.getPosTan(length, pos, tan); imageView.setTranslationX(pos[0]); imageView.setTranslationY(pos[1]); //同时做透明度动画 imageView.setAlpha(animatedValue); if (animatedValue >= 0.5f) { imageView.setScaleX(0.2f + animatedValue); imageView.setScaleY(0.2f + animatedValue); } } }); valueAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { //动画执行完就移除View removeView(imageView); } }); valueAnimator.start(); }5.三阶赛贝尔曲线的计算 下面以左边的为例
这里我也没有更好的办法去计算 是通过不断预估尝试出来的 假如有大佬在这里有很好的计算方法 请务必告知下
dome_08
private Path createLeftPath() { Path path = new Path(); float nextFloat = new Random().nextFloat(); path.moveTo(nextFloat, -height * 1.0f / 1.8f); //曲线控制点一 controlPointOne.x = -(viewWidth); controlPointOne.y = -height / 5; //曲线控制点二 controlPointTwo.x = -(viewWidth + marginLeft / 2); controlPointTwo.y = (int) (-height * 0.15); //生成三阶贝塞尔曲线 path.cubicTo(controlPointOne.x, controlPointOne.y, controlPointTwo.x, controlPointTwo.y, 0, 0); return path; }最后连贯起来看看效果
demo_07
6.最后使用RxJava 的timer()操作符 发推迟消息来让整个动画循环执行起来
这里也可以用handler 来发消息解决
public void startAnimation(int innerDelay) { subscribe = Observable.timer(innerDelay, TimeUnit.MILLISECONDS) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Consumer<Long>() { @Override public void accept(Long aLong) throws Exception { if (createAnimView()) return; int duration = (int) (1500 * Math.random()); if (duration < 500) { duration = 500; } //循环调用 startAnimation(500 + duration); } }); } //动画执行的少量开关操作 public void stopAnimator() { isStop = false; if (null != subscribe) { subscribe.dispose(); } }
demo_08
到这里整个动画流程到这里就结束了,当然在内存的管理上还没有做到极致 大家可以去自由发挥, 希望这篇水文能帮助到那些有相似需求的同学,我们应该把时间拿去做少量更有用的事情,不过截止到目前 马蜂窝最新版 已经没有该头像的泡泡动画,想必他们也改了吧!
说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 高仿马蜂窝旅游头像泡泡动画
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 高仿马蜂窝旅游头像泡泡动画