当pm对上一版的马蜂窝头像泡泡动画审美疲劳后,这次又觉得淘票票的头像动画好看,然后。。。 先看看效果吧!
效果原理分析
- 布局排列
这里可以同自定义View 或继承ViewGroup去实现 不过自定义View复杂度会高很多 我这里也是继承FrameLayout 通过添加和排列ImageView去实现的
- 动画过程
上图已经把整个过程描述很清楚,剩下就是控制动画不断循环执行 以及控制动画的停止、开始 、快慢等一系列操作了
####具体实现
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(ArrayListdrawables) { 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() { @Override public void accept(Long aLong) throws Exception { startAnimations(); } }); }//动画停止操作 public void stopAnimator() { stopAnimator = false; if (null != subscribe) { subscribe.dispose(); subscribe = null; } } 复制代码
最终效果