当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(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;
}
}
最终效果