老司机,不存在的,其实我还算不上,哈哈。。。

最近公司产品突然有一个类似支付宝蚂蚁森林的功能,大致功能跟支付宝蚂蚁森林相像,在看了一下支付宝蚂蚁森林的效果之后,本来这种东西用RN实现是最好不过的,不过产品既然决定选择原生,我们也不能把产品打死是吧,谁让我们是搬砖的呢?趁效果图还没出来,先撸一个控件出来,等公司效果图出来之后就可以放上去直接使用。

首先我们先大致看下支付宝的蚂蚁森林效果图:

这是目前我实现的效果图:

当我们拿到这个需求时先分析一波,不要忙着就动手开干,不然容易平地翻车。。需要实现的功能有:

1、自定小圆球,圆球内文字、上下浮动、消失动画;

2、根据数据动态添加小球,并且位置随机分布在小树周围,不能重叠。这点是最重要的,涉及到一个随机位置生成算法的设计。

好了,当我们确定了我们要实现的功能之后就可以逐步开始撸代码了。

自定义圆球

这个比较容易实现,绘制一个圆,再在园内绘制文字,动画实现统一采用的是属性动画来实现,代码如下,注释写的比较详细就不一一解释了,懒...

/** * @author: xiaohaibin. * @time: 2018/1/5 * @mail:xhb_199409@163.com * @github:https://github.com/xiaohaibin * @describe: 自定义仿支付宝蚂蚁森林水滴View */ public class WaterView extends View { private Paint paint; private ObjectAnimator mAnimator; /** * 文字颜色 */ private int textColor = Color.parseColor("#69c78e"); /** * 水滴填充颜色 */ private int waterColor = Color.parseColor("#c3f593"); /** * 球描边颜色 */ private int storkeColor = Color.parseColor("#69c78e"); /** * 描边线条宽度 */ private float strokeWidth = 0.5f; /** * 文字字体大小 */ private float textSize = 36; /** * 根据远近距离的不同计算得到的应该占的半径比例 */ private float proportion; /** * 水滴球半径 */ private int mRadius = 30; /** * 圆球文字内容 */ private String textContent="3g"; public WaterView(Context context) { super(context); init(); } public WaterView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } public WaterView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { paint = new Paint(); paint.setAntiAlias(true); } @Override public void draw(Canvas canvas) { super.draw(canvas); drawCircleView(canvas); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); setMeasuredDimension(Utils.dp2px(getContext(), (int) (2 * (mRadius+strokeWidth))),Utils.dp2px(getContext(), (int) (2 * (mRadius+strokeWidth)))); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); start(); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); stop(); } @Override protected void onVisibilityChanged(@NonNull View changedView, int visibility) { super.onVisibilityChanged(changedView, visibility); if (visibility == VISIBLE) { start(); } else { stop(); } } private void drawCircleView(Canvas canvas){ //圆球 paint.setColor(waterColor); paint.setStyle(Paint.Style.FILL); canvas.drawCircle(Utils.dp2px(getContext(), mRadius), Utils.dp2px(getContext(), mRadius), Utils.dp2px(getContext(), mRadius), paint); //描边 paint.setColor(storkeColor); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(Utils.dp2px(getContext(), (int) strokeWidth)); canvas.drawCircle(Utils.dp2px(getContext(), mRadius), Utils.dp2px(getContext(), mRadius), Utils.dp2px(getContext(), (int) (mRadius+strokeWidth)) , paint); //圆球文字 paint.setTextSize(textSize); paint.setColor(textColor); paint.setStyle(Paint.Style.FILL); drawVerticalText(canvas, Utils.dp2px(getContext(), mRadius), Utils.dp2px(getContext(), mRadius), textContent); } private void drawVerticalText(Canvas canvas, float centerX, float centerY, String text) { Paint.FontMetrics fontMetrics = paint.getFontMetrics(); float baseLine = -(fontMetrics.ascent + fontMetrics.descent) / 2; float textWidth = paint.measureText(text); float startX = centerX - textWidth / 2; float endY = centerY + baseLine; canvas.drawText(text, startX, endY, paint); } public void start() { if (mAnimator == null) { mAnimator = ObjectAnimator.ofFloat(this, "translationY", -6.0f, 6.0f, -6.0f); mAnimator.setDuration(3500); mAnimator.setInterpolator(new LinearInterpolator()); mAnimator.setRepeatMode(ValueAnimator.RESTART); mAnimator.setRepeatCount(ValueAnimator.INFINITE); mAnimator.start(); } else if (!mAnimator.isStarted()) { mAnimator.start(); } } public void stop() { if (mAnimator != null) { mAnimator.cancel(); mAnimator = null; } } public float getProportion() { return proportion; } public void setProportion(float proportion) { this.proportion = proportion; } }

动态随机添加小球

这里我采用的是集成FrameLayout 通过设置小球数据,动态将小球add进去,比较简便,在这里最重要是动态随机添加小球的算法,解决了这个算法就好办了。通过仔细观察支付宝蚂蚁森林的效果实现,我们可以发现一般小球都是在树的正上方随机分布的。所以我想以小树的根为中心,小树的高度为半径为一个扇形,在这个扇形上方随机摆放小球。

公式:坐标 = 旋转角度 * 半径 * 根据远近距离的不同计算得到的应该占的半径比例

圆上任一点(x1,y1)坐标的计算公式:

x1 = x0 + r * cos(ao * 3.14 /180 )

y1 = y0 + r * sin(ao * 3.14 /180 )

JAVA学习交流QQ群:288351179

具体实现代码如下:

/** * @author: xiaohaibin. * @time: 2018/1/5 * @mail:xhb_199409@163.com * @github:https://github.com/xiaohaibin * @describe: 支付宝蚂蚁森林水滴能量 */ public class WaterFlake extends FrameLayout { private OnWaterItemListener mOnWaterItemListener; /** * 小树坐标X */ private float treeCenterX = 0; /** * 小树坐标Y */ private float treeCenterY = 0; /** * 小树高度 */ private int radius = 80; /** * 开始角度 */ private double mStartAngle = 0; /** * 是否正在收集能量 */ private boolean isCollect = false; public WaterFlake(@NonNull Context context) { super(context); } public WaterFlake(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public WaterFlake(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { int x = (int) event.getX(); int y = (int) event.getY(); Rect rect = new Rect(); for (int i = 0; i < getChildCount(); i++) { getChildAt(i).getHitRect(rect); if (rect.contains(x, y)) { if (mOnWaterItemListener != null) { getChildAt(i).performClick(); mOnWaterItemListener.onItemClick(i); startAnimator(getChildAt(i)); return true; } } } } return super.onTouchEvent(event); } @Override public boolean performClick() { return super.performClick(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); measureChildren(widthMeasureSpec, heightMeasureSpec); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int childCount = getChildCount(); if (childCount==0){ return; } int left, top; // 根据tem的个数,计算角度 float angleDelay = -180 / childCount; for (int i = 0; i < childCount; i++) { WaterView child = (WaterView) getChildAt(i); mStartAngle %= 180; //设置CircleView小圆点的坐标信息 //坐标 = 旋转角度 * 半径 * 根据远近距离的不同计算得到的应该占的半径比例 // 则圆上任一点为:(x1,y1) // x1 = x0 + r * cos(ao * 3.14 /180 ) // y1 = y0 + r * sin(ao * 3.14 /180 ) if (child.getVisibility() != GONE) { left = (int) (getTreeCenterX() + radius * Math.cos(mStartAngle * 3.14 / 180) * (child.getProportion() / radius * 2)); top = (int) (getTreeCenterY() + radius * Math.sin(mStartAngle * 3.14 / 180) * (child.getProportion() / radius * 2)); child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredWidth()); } mStartAngle += angleDelay; } } /** * 设置小球数据,根据数据集合创建小球数量 * * @param modelList 数据集合 */ public void setModelList(List<WaterModel> modelList, float treeCenterX, float treeCenterY) { this.treeCenterX = treeCenterX; this.treeCenterY = treeCenterY; for (int i = 0; i < modelList.size(); i++) { WaterView waterView = new WaterView(getContext(),(i+1)+"g"); waterView.setProportion(Utils.getRandom(radius, radius + 80)); addView(waterView); } } /** * 设置小球点击事件 * * @param onWaterItemListener */ public void setOnWaterItemListener(OnWaterItemListener onWaterItemListener) { mOnWaterItemListener = onWaterItemListener; } public interface OnWaterItemListener { void onItemClick(int pos); } private void startAnimator(final View view) { if (isCollect) { return; } isCollect = true; ObjectAnimator translatAnimatorY = ObjectAnimator.ofFloat(view, "translationY", getTreeCenterY()); translatAnimatorY.start(); ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f); alphaAnimator.start(); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.play(translatAnimatorY).with(alphaAnimator); animatorSet.setDuration(3000); animatorSet.start(); animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { removeViewInLayout(view); isCollect = false; } }); } public float getTreeCenterX() { return treeCenterX; } public float getTreeCenterY() { return treeCenterY; } }

小球摆放随机算法有多种实现方式,这只是其中一种,写的不好的地方,还望各位指正, 如果你想跳槽换工作 但是技术又不够 或者工作上遇到了瓶颈 我这里有一个JAVA的免费直播课程 讲的是高端的知识点基础不好的勿入哟 只要你有1-5年的开发经验可以加群找我要课堂链接 注意:是免费的 没有开发经验勿入哦)欢迎大家一起交流学习。

老司机带你顺手撸一个支付宝蚂蚁森林效果相关推荐

  1. 手撸一个仿蚂蚁森林微信小程序

    每天逛逛CSDN,看看大牛们的技术文章,查找自己想了解的知识,是我必做的事情. 每天到支付宝看看自己的余额,看看自己的33块钱还在吗?顺便到蚂蚁森林收下自己和好友的能量是我必做的事.看着自己的能量又被 ...

  2. 仿支付宝蚂蚁森林效果

    CustomWaterView 项目地址:xiaohaibin/CustomWaterView  简介::star: 仿支付宝蚂蚁森林效果 更多:作者   提 Bug 标签: 实现原理文章:https ...

  3. Android 仿支付宝蚂蚁森林动画效果

    Android 动画可以归纳为以下几种: 视图动画(View 动画) 帧动画(Frame 动画.Drawable 动画) 属性动画 触摸反馈动画(Ripple Effect) 揭露动画(Reveal ...

  4. 利用Auto.js自动收集支付宝蚂蚁森林能量小程序

    最近利用Auto.js写了一个支付宝蚂蚁森林自动收集自己能量和好友能量的小程序,现在发布出来共享给大家.程序在我的华为手机上运行效果非常好. //1.解锁屏幕 unlock(); //2.启用按键监听 ...

  5. 老司机带你从源码开始撸Spring生命周期!!!

    导读 Spring在Java Web方面有着举足轻重的地位,spring的源码设计更是被很多开发者所惊叹,巧妙的设计,精细的构思,都注定他的地位.今天陈某大言不惭的带你来从源码角度解析Spring的生 ...

  6. 老司机带你玩转SDL(一)

    老司机带你玩转SDL --第一站"缘由" 啦啦啦,啦啦啦 我是SDL的老司机 大厂小厂曾呆过 今天的内容真正好 带着大家把SDL玩转了 ---- 伴随着类似聂耳<卖报歌> ...

  7. 老司机带你玩转面试(2):Redis 过期策略以及缓存雪崩、击穿、穿透

    前文回顾 建议前一篇文章没看过的同学先看下前面的文章: 「老司机带你玩转面试(1):缓存中间件 Redis 基础知识以及数据持久化」 过期策略 Redis 的过期策略都有哪些? 在聊这个问题之前,一定 ...

  8. 十年建站老司机带你十分钟搭建网站

    十年建站老司机带你十分钟搭建网站 本文概要: 1. 域名 + 域名注册 + 域名备案 + 域名解析绑定 2. 服务器 + 虚拟主机 + windows服务器 + linux服务器 3. CMS建站系统 ...

  9. Java用map实现沁园春_QQ飞车:沁园春地图车神跑法,实力老司机带你了解这几个技巧...

    原标题:QQ飞车:沁园春地图车神跑法,实力老司机带你了解这几个技巧 QQ手游飞车上线以来收获了一大堆的迷妹迷弟,小编当然也是其中之一啦,小编在这里给大家带来最新的游戏资讯,和各种模式以及赛道的解说. ...

最新文章

  1. Java 8 Lambda
  2. 华为鸿蒙系统内部消息,华为鸿蒙系统内部曝光,并且将于年底发布?网友:还能再假点?...
  3. 局部特征(4)——SIFT和SURF的比较
  4. C语言中编译、生成、调试、运行的区别
  5. 求虚拟机11.0密钥
  6. Algs4-2.1.17动画-插入排序
  7. C#基础加强_泛型的基本原理
  8. HTML的HTTP 中 GET 与 POST 的区别
  9. 雕虫晓技(十) Android超简单气泡效果
  10. python字符串基本操作符有哪些_Python字符串及基本操作(入门必看)!!
  11. [转载] pip快速下载python包
  12. R语言包的升级与降级
  13. VS2010/MFC编程入门系列教程
  14. 使用老毛子在腾讯云DNSpod上实现DDNS
  15. sz命令获取文件到本地路径设置
  16. 历年被3.15晚会曝光的科技企业现状
  17. 为什么电脑安装qq后显示没网络连接服务器,能上QQ电脑却无法上网怎么解决
  18. Spring Cloud中使用jib进行docker部署
  19. 《结构思考力》- 书摘整理
  20. 一个程序员 作者 设计师的 2018 年终总结

热门文章

  1. Parallels Desktop 虚拟机(Red Hat/Centos)配置桥接网络
  2. Bugfree的使用
  3. wifi ap channel
  4. HTML引用js文件且js文件互调
  5. C. Robot in a Hallway(递推/前缀和/动态规划)
  6. Django-慕课网制作(一)
  7. 加速度传感器采集的加速度值有没有必要转换为位移量
  8. Vue 使用 JQuery方法 完美替代 JQuery
  9. 数据库-----数据库操作。
  10. JavaScript本地储存:localStorage、sessionStorage、cookie