高仿淘票票头像动画

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

高仿淘票票头像动画_第1张图片
tpp_01

效果原理分析

  1. 布局排列


    高仿淘票票头像动画_第2张图片
    tpp_02

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

  1. 动画过程


    高仿淘票票头像动画_第3张图片
    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 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));
        }
    }

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

高仿淘票票头像动画_第4张图片
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();
    }

单次动效


高仿淘票票头像动画_第5张图片
tpp_05

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

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

最终效果


高仿淘票票头像动画_第6张图片
tpp_07

你可能感兴趣的:(高仿淘票票头像动画)