高仿淘票票头像动画

作者: 明朗__ | 来源:发表于2019-04-22 20:59 被阅读55次

    当pm对上一版的马蜂窝头像泡泡动画审美疲劳后,这次又觉得淘票票的头像动画好看,然后。。。
    先看看效果吧!

    tpp_01

    效果原理分析

    1. 布局排列


      tpp_02

    这里可以同自定义View 或继承ViewGroup去实现 不过自定义View复杂度会高很多 我这里也是继承FrameLayout 通过添加和排列ImageView去实现的

    1. 动画过程


      tpp_03

    上图已经把整个过程描述很清楚,剩下就是控制动画不断循环执行 以及控制动画的停止、开始 、快慢等一系列操作了

    具体实现

    1. 布局的初始化排列相关

    public class AmoyTicketLayout extends FrameLayout {
       public AmoyTicketLayout(@NonNull Context context) {
            this(context, null);
        }
    
        public AmoyTicketLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public AmoyTicketLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initView(context);
        }
        
        private void initView(Context context) {
            eachWidth = SizeUtils.dp2px(context, 35);
            eachMargin = SizeUtils.dp2px(context, 7);
            distance = eachWidth - eachMargin;
            //先添加0号view
            initFirstView();
            //循环添加其余子View 
            for (int i = 5; i >= 0; i--) {
                LayoutParams layoutParams = getLayoutParams(i);
                ImageView roundedImageView = getImageView();
                addView(roundedImageView, layoutParams);
            }
        }
        //添加0号view 并缩放到最小
        private void initFirstView() {
            ImageView imageView = getImageView();
            imageView.setScaleX(0);
            imageView.setScaleY(0);
            addView(imageView, getLayoutParams(5));
        }
    
        private ImageView getImageView() {
            ImageView roundedImageView = new ImageView(getContext());
            roundedImageView.setScaleType(ImageView.ScaleType.FIT_XY);
            return roundedImageView;
        }
        //每个子View的marginRight距离是相同的 
        private LayoutParams getLayoutParams(int i) {
            LayoutParams layoutParams = new LayoutParams(eachWidth,eachWidth);
            layoutParams.gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL;
            int marginRight = (distance) * i;
            layoutParams.setMargins(0, 0, marginRight, 0);
            return layoutParams;
        }
    
    }
    

    数据初始化

    public void initData(ArrayList<Drawable> drawables) {
            if (null == drawables || drawables.isEmpty()) return;
    
            int childCount = getChildCount();
            if (childCount == 0) return;
    
            int size = drawables.size();
            if (size < childCount) return;
    
            this.drawables = drawables;
            //记录图片资源取到哪里 用于循环时标记使用
            position = childCount - 1;
    
            for (int i = 0; i < childCount; i++) {
                //以为布局中6号为最后一个View  但是要求显示的要是第一个图片
                ImageView imageView = (ImageView) getChildAt(childCount -(i + 1));
                imageView.setBackground(drawables.get(i));
            }
        }
    

    由上操作就完成布局初始排列 和图片资源的加载显示

    tpp_04

    2. 动画的具体实现

     public void startAnimations() {
            if (!stopAnimator) return;
    
            if (null == drawables || drawables.isEmpty()) {
                stopAnimator = false;
                return;
            }
            ValueAnimator valueAnimator = ValueAnimator.ofFloat(1.0f, 0.0f);
            valueAnimator.setDuration(1000);
            valueAnimator.setInterpolator(new LinearInterpolator());
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float animatedValue = (float) animation.getAnimatedValue();
                    int childCount = getChildCount() - 1;
                    float v = 1.0f - animatedValue;
                    //计算每个单元平移的距离
                    float translationX = distance * (v);
    
                    for (int i = 0; i < getChildCount(); i++) {
                        ImageView childView = (ImageView) getChildAt(i);
                        if (i == childCount) {//当view为最后一个时 也就是6号 做缩小操作
                            childView.setScaleX(animatedValue);
                            childView.setScaleY(animatedValue);
                        } else if (i == 0) {//当view为第一个时 也就是0号 做放大操作
                            childView.setScaleX(v);
                            childView.setScaleY(v);
                        } else {//其他view 就通不断改变marginRight 来做平移动作
                            FrameLayout.LayoutParams layoutParams = (LayoutParams) childView.getLayoutParams();
                            int marginRight = (distance) * (childCount - i);
                            layoutParams.setMargins(0, 0, (int) (marginRight - translationX), 0);
                            childView.setLayoutParams(layoutParams);
                        }
                    }
                }
            });
            valueAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationStart(Animator animation) {
                    //动画开始确定最后一个View的缩放中心点
                    int childCount = getChildCount() - 1;
                    ImageView imageView = (ImageView) getChildAt(childCount);
                    imageView.setPivotX(eachWidth);
                    imageView.setPivotY(eachWidth / 2);
                }
    
                @Override
                public void onAnimationEnd(Animator animation) {
                    int childCount = getChildCount() - 1;
                    //动画结束删除最后一个View  也就是6号
                    removeViewAt(childCount);
                    
                    //确定获取图片资源的index
                    position++;
                    if (position >= drawables.size()) {
                        position = 0;
                    }
                    
                    ImageView imageView = getImageView();
                    imageView.setBackground(drawables.get(position));
                    imageView.setScaleX(0);
                    imageView.setScaleY(0);
                    //动画结束 创建0号View 放到1号后面 其他view的index 将全部加1
                    addView(imageView, 0, getLayoutParams(5));
                    //再次启动动画
                    startAnimation();
    
                }
            });
            valueAnimator.start();
        }
    

    单次动效


    tpp_05

    利用Rxjava的timer()实现循环轮播效果

    //动画开始  
    private void startAnimation() {
            subscribe = Observable.timer(500, TimeUnit.MILLISECONDS)
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Consumer<Long>() {
                        @Override
                        public void accept(Long aLong) throws Exception {
                            startAnimations();
                        }
                    });
        }
    //动画停止操作    
    public void stopAnimator() {
            stopAnimator = false;
            if (null != subscribe) {
                subscribe.dispose();
                subscribe = null;
            }
        }    
    

    最终效果


    tpp_07

    相关文章

      网友评论

        本文标题:高仿淘票票头像动画

        本文链接:https://www.haomeiwen.com/subject/roxpgqtx.html