高仿淘票票頭像動畫

當pm對上一版的馬蜂窩頭像泡泡動畫審美疲勞後,這次又覺得淘票票的頭像動畫好看,然後。。。
先看看效果吧!

效果原理分析

  1. 佈局排列


這裏可以同自定義View 或繼承ViewGroup去實現 不過自定義View複雜度會高很多 我這裏也是繼承FrameLayout 通過添加和排列ImageView去實現的

  1. 動畫過程


上圖已經把整個過程描述很清楚,剩下就是控制動畫不斷循環執行 以及控制動畫的停止、開始 、快慢等一系列操作了

具體實現

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));
        }
    }

由上操作就完成佈局初始排列 和圖片資源的加載顯示

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();
    }

單次動效


利用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;
        }
    }    

最終效果


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章