先看一下动画效果:第一次上传gif图片 先录的mp4视频 后来转的gif
Android动画在这里就不详细讲了,网上一大把,不清楚的可以去看看。这里只讲这个动画的视线逻辑。
这个动画这里分四个部分:
1.最底部图片心跳动画
2.水波纹扩散动画
3.头像出现的时机动画
4.中间个人头像动画
在这里先讲第一部分:最底部图片心跳动画
int size = mList.length;
animPoints = new Animation[size];
/**
* 五个小点 动画显示
* */
for (int i = 0; i < size; i++) {
final int j = i;
LayoutParams mSmallCircleParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
if (j == 0) {
mSmallCircleParams.rightMargin = ResUtil.getDimens(mContext, R.dimen.common_padding_140);
mSmallCircleParams.gravity = Gravity.RIGHT;
mSmallCircleParams.topMargin = ResUtil.getDimens(mContext, R.dimen.common_padding_380);
} else if (j == 1) {
mSmallCircleParams.rightMargin = ResUtil.getDimens(mContext, R.dimen.common_padding_75);
mSmallCircleParams.gravity = Gravity.RIGHT;
mSmallCircleParams.topMargin = ResUtil.getDimens(mContext, R.dimen.common_padding_89);
} else if (j == 2) {
mSmallCircleParams.leftMargin = ResUtil.getDimens(mContext, R.dimen.common_padding_17);
mSmallCircleParams.topMargin = ResUtil.getDimens(mContext, R.dimen.common_padding_150);
} else if (j == 3) {
mSmallCircleParams.leftMargin = ResUtil.getDimens(mContext, R.dimen.common_padding_39);
mSmallCircleParams.topMargin = ResUtil.getDimens(mContext, R.dimen.common_padding_240);
} else if (j == 4) {
mSmallCircleParams.rightMargin = ResUtil.getDimens(mContext, R.dimen.common_padding_112);
mSmallCircleParams.gravity = Gravity.RIGHT;
mSmallCircleParams.topMargin = ResUtil.getDimens(mContext, R.dimen.common_padding_224);
}
ImageView mSmallCircleView = new ImageView(getContext());
mSmallCircleView.setImageResource(mList[j]);
addView(mSmallCircleView, mSmallCircleParams);
/**加载动画*/
animPoints[j] = AnimationUtils.loadAnimation(getContext(), R.anim.anim_the_heartbeat);
mSmallCircleView.startAnimation(animPoints[j]);
animPoints[j].setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
if (mAnimationProgressListener != null) {
mAnimationProgressListener.startAnimation();
}
}
@Override
public void onAnimationEnd(Animation animation) {
if (mAnimationProgressListener != null) {
mAnimationProgressListener.endAnimation();
}
if (null != animPoints && null != animPoints[j]) {
animPoints[j].cancel();
}
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
}
在这里先讲第二部分:水波纹扩散动画
//布局 管理器,让圆剧中显示
LayoutParams rippleParams = new LayoutParams((int) (2 * (rippleRadius + rippleStrokeWidth * 3)), (int) (2 * (rippleRadius + rippleStrokeWidth * 3)));
rippleParams.gravity = Gravity.CENTER;
//动画的集合
ArrayList animatorList = new ArrayList<>();
//水波纹 缩放、渐变动画
for (int i = 0; i < rippleAmount; i++) {
RipplView rippleView = new RipplView(getContext());
addView(rippleView, rippleParams);
rippleViewList.add(rippleView);
//伸缩动画
float rippleScale = 6.0f;
final ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(rippleView, "ScaleX", 1.0f, rippleScale);
scaleXAnimator.setRepeatCount(ValueAnimator.INFINITE);
scaleXAnimator.setRepeatMode(ObjectAnimator.RESTART);
scaleXAnimator.setStartDelay(i * rippleDelay);
animatorList.add(scaleXAnimator);
final ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(rippleView, "ScaleY", 1.0f, rippleScale);
scaleYAnimator.setRepeatCount(ValueAnimator.INFINITE);
scaleYAnimator.setRepeatMode(ObjectAnimator.RESTART);
scaleYAnimator.setStartDelay(i * rippleDelay);
animatorList.add(scaleYAnimator);
//透明度动画
final ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(rippleView, "Alpha", 1.0f, 0f);
alphaAnimator.setRepeatCount(ValueAnimator.INFINITE);
alphaAnimator.setRepeatMode(ObjectAnimator.RESTART);
alphaAnimator.setStartDelay(i * rippleDelay);
animatorList.add(alphaAnimator);
}
//开始动画
animatorSet.playTogether(animatorList);
//动画的监听
animatorSet.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
if (mAnimationProgressListener != null) {
mAnimationProgressListener.startAnimation();
}
}
@Override
public void onAnimationEnd(Animator animator) {
if (mAnimationProgressListener != null) {
mAnimationProgressListener.endAnimation();
}
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
在这里讲第三部分:头像出现的时机动画
简单的理解就是划分了六个区域:
private final static int showNum = 5;
private static long delayMillis = 4000;
private int radiusP;
private int radiusC;
private int radiusB;
private int pointCX;
private int pointCY;
private int page = 0;
private int lastNum = 0;
private boolean isAddShowing = false;
private List mGodHeadPhoto = new ArrayList<>();
private Handler mHandler = new Handler();
private UserRunnable userRunnable;
private CircleImageView[] imageViews;
private ObjectAnimator[] animator;
/**
* 设置大神头像
*/
public void setUserPhoto(List godHeadPhoto) {
Log.e(TAG, "setUserPhoto==11==" + (null == godHeadPhoto ? 0 : godHeadPhoto.size()));
if (null == godHeadPhoto || godHeadPhoto.isEmpty()) {
return;
}
mGodHeadPhoto.addAll(godHeadPhoto);
Log.e(TAG, "setUserPhoto==22==" + mGodHeadPhoto.size() + "==isAddShowing==" + isAddShowing);
if (isAddShowing) {
return;
}
page = 0;
lastNum = 0;
isAddShowing = true;
if (radiusP <= 0 || radiusC <= 0 || radiusB <= 0 || pointCX <= 0 || pointCY <= 0) {
this.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
RippleView.this.getViewTreeObserver().removeOnPreDrawListener(this);
//RippleView宽
int rippleW = RippleView.this.getWidth();
//RippleView高
int rippleH = RippleView.this.getHeight();
radiusP = ResUtil.getDimens(mContext, R.dimen.common_padding_16);
radiusC = ResUtil.getDimens(mContext, R.dimen.common_padding_36);
int padding = ResUtil.getDimens(mContext, R.dimen.common_padding_20);
radiusB = rippleW / 2 - radiusP - padding;
pointCX = rippleW / 2;
pointCY = rippleH / 2;
startShowUserList(true);
return true;
}
});
} else {
startShowUserList(true);
}
}
/**
* 开始显示
*
* @param isFirst 是否第一次显示
*/
private void startShowUserList(boolean isFirst) {
if (isHidden || (!isFirst && isAddShowing)) {
return;
}
isAddShowing = true;
showNextUserList();
}
/**
* 获得当前显示大神list
*
* @return
*/
private List getCurShowList() {
if (null == mGodHeadPhoto || mGodHeadPhoto.isEmpty()) {
return null;
}
int size = mGodHeadPhoto.size();
if (showNum >= size) {
page = 0;
lastNum = size;
return mGodHeadPhoto;
}
int start = lastNum + page * showNum;
if (start >= size) {
return null;
}
int end = lastNum + (page + 1) * showNum;
if (end >= size) {
//列表已经展示结束
page = 0;
if (end == size) {
lastNum = 0;
return mGodHeadPhoto.subList(start, size);
} else {
lastNum = end - size;
List list = new ArrayList<>();
list.addAll(mGodHeadPhoto.subList(start, size));
list.addAll(mGodHeadPhoto.subList(0, lastNum));
return list;
}
} else {
page++;
return mGodHeadPhoto.subList(start, end);
}
}
/**
* @param radiusP 五个随机大神头像半径
* @param radiusC 中心点圆半径
* @param radiusB 显示区域半径
* @param pointCX 中心点x坐标
* @param pointCY 中心点y坐标
*/
private void addViewPhoto(int radiusP, int radiusC, int radiusB, int pointCX, int pointCY, List list) {
Log.e(TAG, "addViewPhoto==" + (null == list ? 0 : list.size()) + "==lastNum==" + lastNum + "==page==" + page + "==mGodHeadPhoto.size==" + mGodHeadPhoto.size());
if (null != imageViews) {
for (int i = 0; i < imageViews.length; i++) {
if (null != imageViews[i]) {
RippleView.this.removeView(imageViews[i]);
}
}
}
if (userRunnable != null) {
mHandler.removeCallbacks(userRunnable);
}
int size = null == list ? 0 : list.size();
if (size == 0) {
isAddShowing = false;
animPhotos = null;
imageViews = null;
return;
}
animPhotos = new Animation[size];
imageViews = new CircleImageView[size];
animator = new ObjectAnimator[size];
//动画控件的宽高
for (int i = 0; i < size; i++) {
final int k = i;
int x1;
int y1;
int x2;
int y2;
if (k == 0) {
/**随机按钮分四片区域显示
* 左上角区域
*/
x1 = pointCX - radiusB;
y1 = pointCY - radiusB;
x2 = pointCX - radiusP;
y2 = pointCY - radiusC - radiusP;
} else if (k == 1) {
/**随机按钮分四片区域显示
* 右上角区域
*/
x1 = pointCX + radiusP;
y1 = pointCY - radiusB;
x2 = pointCX + radiusB;
y2 = pointCY - radiusC;
} else if (k == 2) {
/**随机按钮分四片区域显示
* 左下角区域
*/
x1 = pointCX - radiusB;
y1 = pointCY + radiusC + radiusP;
x2 = pointCX - radiusP;
y2 = pointCY + radiusB;
} else if (k == 3) {
/**随机按钮分四片区域显示
* 右下角区域
*/
x1 = pointCX + radiusP;
y1 = pointCY + radiusC + radiusP;
x2 = pointCX + radiusB;
y2 = pointCY + radiusB;
} else {
int yshu = getRandomInt(Integer.MAX_VALUE) % 2;
if (yshu == 0) {
/**随机按钮分四片区域显示
* 左中区域
*/
x1 = pointCX - radiusB;
y1 = pointCY - radiusC + radiusP;
x2 = pointCX - radiusC - radiusP;
y2 = pointCY + radiusC - radiusP;
} else {
/**随机按钮分四片区域显示 j
* 右中区域
*/
x1 = pointCX + radiusC + radiusP;
y1 = pointCY - radiusC + radiusP;
x2 = pointCX + radiusB;
y2 = pointCY + radiusC - radiusP;
}
}
// Log.e("addView", "addView x ==== == " + ((x2 - x1) / 2 + x1));
// Log.e("addView", "addView y ==== == " + ((y2 - y1) / 2 + y1));
//随机生成一个屏内的位置来显示动画
int xs = 0;
int ys = 0;
boolean flag = true;
while (flag) {
xs = getRandomInt(Integer.MAX_VALUE) % (x2 - x1) + x1;
ys = getRandomInt(Integer.MAX_VALUE) % (y2 - y1) + y1;
if (isCricle(xs, ys)) {
flag = false;
}
}
final int x = xs;
final int y = ys;
final String pathPhoto = list.get(k);
// Log.e("addView", "addView x === " + x);
// Log.e("addView", "addView y === " + y);
mHandler.postDelayed(new WaitPhotoRunnable(pathPhoto, k, x, y), k * 180);
}
showNextUserList();
}
deom下载地址