高仿淘票票头像动画

当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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容

  • 【Android 动画】 动画分类补间动画(Tween动画)帧动画(Frame 动画)属性动画(Property ...
    Rtia阅读 5,953评论 1 38
  • 翻译自“Collection View Programming Guide for iOS” 0 关于iOS集合视...
    lakerszhy阅读 3,723评论 1 22
  • 天天问我,有没有小偷? 我说有啊! 他很担心的说,我好害怕小偷来咱们家啊! 我跟他说没事,把门锁好就行。 他说,那...
    素衣烟霞阅读 344评论 0 1
  • 咨询形式 咨询日期: 2018年7月3日 时长:1.5小时咨询方式:线下面基 个人背景 小T ...
    紫柠檬Nancy阅读 501评论 0 0
  • 我有时会健忘, 不管是重要的还是不重要的。 不管我是有意的还是无意的, 那时你在, 或许你应该提醒我。
    九呀阅读 166评论 0 2