Android模擬實現支付寶螞蟻森林效果
前言
最近公司產品突然有一個類似支付寶螞蟻森林的功能,大致功能跟支付寶螞蟻森林相像,在看了一下支付寶螞蟻森林的效果之后,打算先擼一個控件出來,等公司效果圖出來之后就可以放上去直接使用。
首先我們先大致看下支付寶的螞蟻森林效果圖:
這是目前我實現的效果圖:
當我們拿到這個需求時先分析一波,不要忙著就動手開干,不然容易平地翻車。。
需要實現的功能有:
1、自定小圓球,圓球內文字、上下浮動、消失動畫;
2、根據數據動態(tài)添加小球,并且位置隨機分布在小樹周圍,不能重疊。這點是最重要的,涉及到一個隨機位置生成算法的設計。
好了,當我們確定了我們要實現的功能之后就可以逐步開始擼代碼了。
自定義圓球
這個比較容易實現,繪制一個圓,再在園內繪制文字,動畫實現統一采用的是屬性動畫來實現,代碼如下,注釋寫的比較詳細就不一一解釋了,懶...
/** * @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; } }
動態(tài)隨機添加小球
這里我采用的是集成FrameLayout 通過設置小球數據,動態(tài)將小球add進去,比較簡便,在這里最重要是動態(tài)隨機添加小球的算法,解決了這個算法就好辦了。通過仔細觀察支付寶螞蟻森林的效果實現,我們可以發(fā)現一般小球都是在樹的正上方隨機分布的。所以我想以小樹的根為中心,小樹的高度為半徑為一個扇形,在這個扇形上方隨機擺放小球。
公式:坐標 = 旋轉角度 * 半徑 * 根據遠近距離的不同計算得到的應該占的半徑比例
圓上任一點(x1,y1)坐標的計算公式:
x1 = x0 + r * cos(ao * 3.14 /180 )
y1 = y0 + r * sin(ao * 3.14 /180 )
具體實現代碼如下:
/** * @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; } } /** * 設置小球數據,根據數據集合創(chuàng)建小球數量 * * @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; } }
小球擺放隨機算法有多種實現方式,這只是其中一種,寫的不好的地方,還望各位指正,歡迎大家一起交流學習。
目前代碼還有一些地方需要完善的,逐步更新。。。
TODO
優(yōu)化小球隨機生成算法,保證每個小球盡量不會重疊
到此這篇關于Android模擬實現支付寶螞蟻森林效果的文章就介紹到這了,更多相關Android螞蟻森林內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Android 中build.prop 文件與 getprop 命令
這篇文章主要介紹了Android 中build.prop 文件與 getprop 命令的相關資料,需要的朋友可以參考下2017-06-06Android布局自定義Shap圓形ImageView可以單獨設置背景與圖片
這篇文章主要介紹了Android布局自定義Shap圓形ImageView可以單獨設置背景與圖片 的相關資料,需要的朋友可以參考下2016-01-01實例講解Android App使用自帶的SQLite數據庫的基本方法
這篇文章主要介紹了Android App使用自帶的SQLite數據庫的基本方法,SQLite是一個小巧的內嵌型數據庫,在數據庫需求不大的情況下使用SQLite其實非常有效,需要的朋友可以參考下2016-04-04Android中使用AsyncTask實現文件下載以及進度更新提示
AsyncTask,它使創(chuàng)建需要與用戶界面交互的長時間運行的任務變得更簡單,本篇文章主要介紹了Android中使用AsyncTask實現文件下載以及進度更新提示,有興趣的可以了解一下。2016-12-12android開發(fā)環(huán)境遇到adt無法啟動的問題分析及解決方法
開始研究android開發(fā),搭建開發(fā)環(huán)境的時候就出了問題,真是束手無策2013-02-02