高仿马蜂窝头像泡泡动画

>>强大,10k+点赞的 SpringBoot 后台管理系统竟然出了详细教程!
文章转自:https://www.jianshu.com/p/dd29f1ae5239
高仿马蜂窝头像泡泡动画

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

高仿马蜂窝头像泡泡动画
demo_01

效果分析:

高仿马蜂窝头像泡泡动画
demo_03
  1. 涉及到有多个view在做动画操作 这里需要继承FrameLayout来左父布局 供图片做动画操作

  2. 每个子view的动画路径类似于S型 我这里采用的是三阶贝塞尔曲线和PathMeasure来完成动画运动路径的封装

  3. 每个子view动画执行完后 是移除添加新的view进来 还是回收重新利用 本案例是直接移除再添加新的(回收重新利用还没来得及去考虑该怎么写)

  4. 动画是循环不停的播放 我采用的是RxJava timer()操作符 不断的发送随机延迟消息去通知动画的执行

  5. 最后就剩下一些停止动画操作的开关设定

实现步骤

1.一些基本的初始化工作

 1public class HeadBubbleView extends FrameLayout {
2    //这个position很重要 不断的取出图片资源 靠它累加完成的
3    private int position = 0;
4    public HeadBubbleView(@NonNull Context context) {
5        this(context,null);
6    }
7    public HeadBubbleView(Context context, AttributeSet attrs) {
8        super(context, attrs);
9        mContext = context;
10        setFocusable(false);
11        //三阶贝塞尔曲线控制点一
12        controlPointOne = new Point();
13        //三阶贝塞尔曲线控制点二
14        controlPointTwo = new Point();
15        //每个子view的宽高是固定的
16        viewWidth = viewHeight = SizeUtils.dp2px(context, 22);
17        marginLeft = SizeUtils.dp2px(context, 15);
18        marginBot = SizeUtils.dp2px(context, 21);
19        //父View的高度也是固定的
20        height = SizeUtils.dp2px(context, 130);
21        //用于从PathMeasure 中不断的取出 曲线的路径值
22        pos = new float[2];
23        tan = new float[2];
24        initView();
25    }

2.初始化的时候数据的加载状态

 1private void initView() {
2        //这个ImageView将不执行动画 用于底部不断切换图片展示
3        tempImageView = getImageView();
4        textView = getTextView();
5        initData(tempImageView);
6    } 
7//创建执行动画的具体角色    
8private ImageView getImageView() {
9        LayoutParams layoutParams = new LayoutParams(viewWidth, viewHeight);
10        ImageView roundedImageView = new ImageView(getContext());
11        roundedImageView.setScaleType(ImageView.ScaleType.FIT_XY);
12        layoutParams.gravity = Gravity.BOTTOM | Gravity.END;
13        layoutParams.setMargins(00, marginLeft, marginBot);
14        addView(roundedImageView, layoutParams);
15        return roundedImageView;
16    }
17//创建用于显示坐标xx来过的TextView    
18private TextView getTextView() {
19        int bottom = SizeUtils.dp2px(mContext, 23);
20        int right = SizeUtils.dp2px(mContext, 41);
21        LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
22        layoutParams.gravity = Gravity.END | Gravity.BOTTOM;
23        layoutParams.setMargins(00, right, bottom);
24
25        TextView tv_name = new TextView(mContext);
26        tv_name.setTextSize(12);
27        tv_name.setTextColor(Color.WHITE);
28        addView(tv_name, layoutParams);
29        return tv_name;
30    }
31//第一次加载数据
32private void initData(ImageView roundedImageView{
33        if (null != browseEntities && browseEntities.size() > 0) {
34            //第一次去第0个数据
35            BrowseEntity browseEntity = browseEntities.get(position);
36            if (null != browseEntity) {
37                roundedImageView.setBackgroundResource(browseEntity.drawableId);
38                String username = browseEntity.name;
39                if (!TextUtils.isEmpty(username)) {
40                    textView.setText(username + "来过");
41                }
42            }
43        }
44    }

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

高仿马蜂窝头像泡泡动画
demo_02


3.接下来完成第一阶段动画 由最小缩放到最大

 1private boolean createAnimView() {
2        if (!isStop) {
3            return true;
4        }
5        ImageView imageView = getImageView();
6        //创建好后 设置缩放到最小
7        imageView.setScaleX(0);
8        imageView.setScaleY(0);
9        initData(imageView);
10        startScaleAnim(imageView);
11        return false;
12    }
13//执行缩放动画    
14private void startScaleAnim(final ImageView imageView) {
15        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0.0f1.0f);
16        valueAnimator.setDuration(800);
17        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
18            @Override
19            public void onAnimationUpdate(ValueAnimator animation) {
20                float animatedValue = (float) animation.getAnimatedValue();
21                imageView.setScaleX(0.1f + animatedValue);
22                imageView.setScaleY(0.1f + animatedValue);
23            }
24        });
25        valueAnimator.addListener(new AnimatorListenerAdapter() {
26            @Override
27            public void onAnimationEnd(Animator animation) {
28                if (position == browseEntities.size() - 1) {
29                    position = 0;
30                } else {
31                    position++;
32                }
33          BrowseEntity browseEntity = browseEntities.get(position);
34        //动画执行完后要立马取出下一个图片 把底部的图片显示更新
35        tempImageView.setBackgroundResource(browseEntity.drawableId);
36        //动画执行完执行平移动画       
37        startTranslationAnimator(imageView);
38            }
39        });
40        valueAnimator.start();
41    }
高仿马蜂窝头像泡泡动画
demo_04


4.第二阶段的曲线运动缩小动画

 1private void startTranslationAnimator(final ImageView imageView) {
2        Path path;
3        int seed = (int) (Math.random() * 100);
4        //根据随机数来确定是走左边曲线还是右边曲线
5        if (seed % 2 == 0) {
6            //曲线路径的封装
7            path = createRightPath();
8        } else {
9            //曲线路径的封装
10            path = createLeftPath();
11        }
12        //通过PathMeasure 和ValueAnimator结合 在不同的阶段取出运动路径的x,y值
13        final PathMeasure pathMeasure = new PathMeasure(path, false);
14        final ValueAnimator valueAnimator = ValueAnimator.ofFloat(1.0f0.0f);
15        valueAnimator.setDuration(riseDuration);
16        valueAnimator.setInterpolator(new LinearInterpolator());
17        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
18            @Override
19            public void onAnimationUpdate(ValueAnimator animation) {
20                float animatedValue = (float) animation.getAnimatedValue();
21                int length = (int) (pathMeasure.getLength() * animatedValue);
22               //在不同的阶段取出运动路径的x,y值
23                pathMeasure.getPosTan(length, pos, tan);
24                imageView.setTranslationX(pos[0]);
25                imageView.setTranslationY(pos[1]);
26                //同时做透明度动画
27                imageView.setAlpha(animatedValue);
28                if (animatedValue >= 0.5f) {
29                    imageView.setScaleX(0.2f + animatedValue);
30                    imageView.setScaleY(0.2f + animatedValue);
31                }
32            }
33        });
34        valueAnimator.addListener(new AnimatorListenerAdapter() {
35            @Override
36            public void onAnimationEnd(Animator animation) {
37                //动画执行完就移除View
38                removeView(imageView);
39            }
40        });
41        valueAnimator.start();
42    }

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

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

高仿马蜂窝头像泡泡动画
dome_08
 1private Path createLeftPath() {
2        Path path = new Path();
3        float nextFloat = new Random().nextFloat();
4        path.moveTo(nextFloat, -height * 1.0f / 1.8f);
5        //曲线控制点一
6        controlPointOne.x = -(viewWidth);
7        controlPointOne.y = -height / 5;
8        //曲线控制点二
9        controlPointTwo.x = -(viewWidth + marginLeft / 2);
10        controlPointTwo.y = (int) (-height * 0.15);
11        //生成三阶贝塞尔曲线
12        path.cubicTo(controlPointOne.x, controlPointOne.y, controlPointTwo.x, controlPointTwo.y, 00);
13        return path;
14    }

最后连贯起来看看效果

高仿马蜂窝头像泡泡动画
demo_07

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

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

 1public void startAnimation(int innerDelay) {
2        subscribe = Observable.timer(innerDelay, TimeUnit.MILLISECONDS)
3                .observeOn(AndroidSchedulers.mainThread())
4                .subscribe(new Consumer<Long>() {
5                    @Override
6                    public void accept(Long aLong) throws Exception {
7                        if (createAnimView()) return;
8
9                        int duration = (int) (1500 * Math.random());
10                        if (duration < 500) {
11                            duration = 500;
12                        }
13                        //循环调用
14                        startAnimation(500 + duration);
15                    }
16                });
17    }
18
19//动画执行的一些开关操作  
20public void stopAnimator() {
21        isStop = false;
22        if (null != subscribe) {
23            subscribe.dispose();
24        }
25    }   
高仿马蜂窝头像泡泡动画
demo_08

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

高仿马蜂窝头像泡泡动画


原文始发于微信公众号(Taonce):高仿马蜂窝头像泡泡动画