当pm制定完下一版本需求打开马蜂窝旅游app准备出去嗨一圈的时候 看到了马蜂窝旅游app的一个用户头像动画后。。。(=@__@=) 先看看效果图

效果分析:

  1. 涉及到有多个view在做动画操作 这里需要继承FrameLayout来左父布局 供图片做动画操作
  2. 每个子view的动画路径类似于S型 我这里采用的是三阶贝塞尔曲线和PathMeasure来完成动画运动路径的封装
  3. 每个子view动画执行完后 是移除添加新的view进来 还是回收重新利用 本案例是直接移除再添加新的(回收重新利用还没来得及去考虑该怎么写)
  4. 动画是循环不停的播放 我采用的是RxJava timer()操作符 不断的发送随机延迟消息去通知动画的执行
  5. 最后就剩下一些停止动画操作的开关设定

####实现步骤

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 + "来过");}}}}
复制代码

由上面的操作就完成基础显示

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() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float animatedValue = (float) animation.getAnimatedValue();imageView.setScaleX(0.1f + animatedValue);imageView.setScaleY(0.1f + animatedValue);}});valueAnimator.addListener(new AnimatorListenerAdapter() {@Overridepublic 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();}
复制代码

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() {@Overridepublic 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() {@Overridepublic void onAnimationEnd(Animator animation) {//动画执行完就移除ViewremoveView(imageView);}});valueAnimator.start();}
复制代码

5.三阶赛贝尔曲线的计算 下面以左边的为例

这里我也没有更好的办法去计算 是通过不断预估尝试出来的 如果有大佬在这里有很好的计算方法 请务必告知下

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;}
复制代码

最后连贯起来看看效果

6.最后使用RxJava 的timer()操作符 发延迟消息来让整个动画循环执行起来

这里也可以用handler 来发消息处理

public void startAnimation(int innerDelay) {subscribe = Observable.timer(innerDelay, TimeUnit.MILLISECONDS).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<Long>() {@Overridepublic 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();}}
复制代码

到这里整个动画流程到这里就结束了,当然在内存的管理上还没有做到极致 大家可以去自由发挥, 希望这篇水文能帮助到那些有类似需求的同学,我们应该把时间拿去做一些更有用的事情,不过截止到目前 马蜂窝最新版 已经没有该头像的泡泡动画,想必他们也改了吧!

高仿马蜂窝头像泡泡动画相关推荐

  1. Android高仿微信头像裁剪

    最近公司的APP很多用户反应无法上传头像,于是打算修改原来头像裁剪的代码.参考微信.QQ.唱吧头像裁剪的操作,决定就仿微信头像裁剪来上传用户头像,在Android大神鸿洋的一篇高仿微信头像的博客(博客 ...

  2. Android 高仿微信头像截取 打造不一样的自定义控件

    转载请表明出处: http://blog.csdn.net/lmj623565791/article/details/39761281 ,本文出自: [张鸿洋的博客] 1.概述 前面已经写了关于检测手 ...

  3. Android 高仿微信头像截取 打造不一样的自定义控件

    转载请表明出处: http://blog.csdn.net/lmj623565791/article/details/39761281,本文出自: [张鸿洋的博客] 1.概述 前面已经写了关于检测手势 ...

  4. android10.0 bootanimation系统高仿Iphone苹果开机动画

    android10.0系统高仿Iphone苹果开机动画 直接先上效果图 bootanimation.zip压缩前: part0(文件夹存放要播放的图片格式最好是png24) part1(文件夹) de ...

  5. 高仿支付宝首页头部动画

    高仿支付宝首页头部动画(使用design实现效果,CoordinatorLayout+AppBarLayout+CollapsingToolbarLayout+Toolbar) 效果图(效果图渐变不明 ...

  6. Android高仿马蜂窝Tabbar波浪线

    文章目录 简介 效果 思路 关键代码 先画完整的弧线,图中红色曲线 再画指示器部分的弧线,图中深蓝色弧线 不足 源码[github](https://github.com/Super-Bin/Horn ...

  7. Android:高仿QQ头像截取升级版

    观看此篇文章前,请先阅读上篇文章:高仿QQ头像截取: 本篇之所以为升级版,是在截取头像界面添加了与qq类似的阴影层(裁剪区域以外的部分),且看效果图:   为了适应大家不同需求,这次打了两个包,及上图 ...

  8. 安卓高仿QQ头像截取升级版

    观看此篇文章前,请先阅读上篇文章:高仿QQ头像截取: 本篇之所以为升级版,是在截取头像界面添加了与qq类似的阴影层(裁剪区域以外的部分),且看效果图:   为了适应大家不同需求,这次打了两个包,及上图 ...

  9. Flutter高仿支付宝打钩动画

    文章目录 简介 效果 预备知识 自定义控件基本知识 动画基本知识 动画分析 PathMetric类作用 关键代码 简介 Flutter高仿支付宝打钩动画,利用动画和路径Path实现简易效果.熟悉And ...

最新文章

  1. 自旋电子学与量子计算机,基于“分子自旋电子学”的新技术,将给量子计算机带来新希望!...
  2. Linux的编译器vi之最详细介绍
  3. 倒计时 3 天 | 神策 2019 数据驱动大会即将开幕
  4. 关于SIM800C MINI V4.0 V4版本 5v供电模块重启问题
  5. Java中的日期操作
  6. c语言用队列stl加头文件,C++ STL List队列用法(实例)
  7. 力扣1232.缀点成线
  8. Git仓库只拷贝代码-不拷贝提交记录-不拷贝其他分支
  9. 进程(Process)和线程(Thread)的区别
  10. 波士顿房价预测python决策树_百度飞浆paddlepaddle之波士顿房价预测(二)
  11. 点击按钮返回上一个页面_零基础跟老陈一起学WordPress 《第四课》用WP半小时建一个商业网站...
  12. 全面解析云智慧数据中心统一运管解决方案
  13. 对话系统数据集--CrossWOZ
  14. 【华为OD机试真题 JS】统计射击比赛成绩
  15. Expressive Body Capture: 3D Hands, Face, and Body from a Single Image
  16. Shamir门限秘密共享方案 秘密分配及还原过程详解 【橘小白】
  17. 中国大学计算机专业排名教育部,全国计算机专业学校排名!别选错学校了
  18. 兼容QQ浏览器,UC浏览器滚动到底部
  19. Xdebug中文文档-安装
  20. 随笔记——多线程使用及注意点

热门文章

  1. 英语翻译软件-批量自动免费翻译软件支持三方接口翻译
  2. 搜索淘宝/天猫店铺列表 API 接口
  3. AD17PCB 导出CAD文件
  4. 数学建模之找数据网站
  5. 笔记:fopen函数用法详解
  6. 麦当劳理论-打破第一层壁垒
  7. bzoj4827: [Hnoi2017]礼物 FFT
  8. tomcat服务器启动不成功解决办法,详解,手把手教程
  9. JavaScript基本数据类型与引用数据类型
  10. commonjs使用 范例