當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;
}
}
最終效果