前言
在上一篇 动画实战:打造炫酷的加载动画 中,我们利用属性动画打造了较为炫酷的加载动画,本篇文章还是以属性动画为利器,结合自定义 View/ViewGroup 打造仿某搜索引擎搜索中的动画效果。
1.效果分析
1.1 效果图
1.2 效果分析
三个点,每次回到中间会交换颜色,属性动画控制运动
2.实现分析
RelativeLayout里面添加3个View(圆形),结合属性动画去反复运动,监听动画执行在合适的时间去交换颜色
画圆:CircleView
public class CircleView extends View {
private Paint mPaint;
private int mColor;
public CircleView(Context context) {
this(context,null);
}
public CircleView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public CircleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
}
@Override
protected void onDraw(Canvas canvas) {
// 画背景圆形 三个参数 cx cy 半径 画笔
int cx = getWidth()/2;
int cy = getHeight()/2;
canvas.drawCircle(cx,cy,cx,mPaint);
}
/**
* 切换颜色
* @param color
*/
public void exchangeColor(int color){
mColor = color;
mPaint.setColor(color);
invalidate();
}
/**
* 获取当前的颜色
* @return
*/
public int getColor() {
return mColor;
}
}
实现加载动画的 View:LoadingView
public class LoadingView extends RelativeLayout{
private CircleView mLeftView,mMiddleView,mRightView;
private int mTranslationDistance = 30;
private final long ANIMATION_TIME = 350;
private boolean mIsStopAnimator = false;
public LoadingView(Context context) {
this(context,null);
}
public LoadingView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public LoadingView(Context context, AttributeSet attrs, int defStyleAttr) {// LayoutInflater里面
super(context, attrs, defStyleAttr);
mTranslationDistance = dip2px(mTranslationDistance);
setBackgroundColor(Color.WHITE);
// 添加三个View 但是要圆形
mLeftView = getCircleView(context);
mLeftView.exchangeColor(Color.BLUE);
mMiddleView = getCircleView(context);
mMiddleView.exchangeColor(Color.RED);
mRightView = getCircleView(context);
mRightView.exchangeColor(Color.GREEN);
addView(mLeftView);
addView(mRightView);
addView(mMiddleView);
post(new Runnable() {
@Override
public void run() {
// 让布局实例化好之后再去开启动画
expendAnimation();
}
});
}
/**
* 展开动画
*/
private void expendAnimation() {
if (mIsStopAnimator) {
return;
}
// 左边跑
ObjectAnimator leftTranslationAnimator = ObjectAnimator.ofFloat(mLeftView,"translationX",0,-mTranslationDistance);
// 右边跑
ObjectAnimator rightTranslationAnimator = ObjectAnimator.ofFloat(mRightView,"translationX",0,mTranslationDistance);
// 弹性效果 荡秋千一样 差值器 刚开始快越来越慢
AnimatorSet set = new AnimatorSet();
set.setDuration(ANIMATION_TIME);
set.playTogether(leftTranslationAnimator,rightTranslationAnimator);
set.setInterpolator(new DecelerateInterpolator());
set.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// 往里面跑
innerAnimation();
}
});
set.start();
}
private void innerAnimation() {
if (mIsStopAnimator) {
return;
}
// 左边跑
ObjectAnimator leftTranslationAnimator = ObjectAnimator.ofFloat(mLeftView,"translationX",-mTranslationDistance,0);
// 右边跑
ObjectAnimator rightTranslationAnimator = ObjectAnimator.ofFloat(mRightView,"translationX",mTranslationDistance,0);
AnimatorSet set = new AnimatorSet();
set.setInterpolator(new AccelerateInterpolator());
set.setDuration(ANIMATION_TIME);
set.playTogether(leftTranslationAnimator,rightTranslationAnimator);
set.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// 往里外面跑
// 切换颜色顺序 左边的给中间 中间的给右边 右边的给左边
int leftColor = mLeftView.getColor();
int rightColor = mRightView.getColor();
int middleColor = mMiddleView.getColor();
mMiddleView.exchangeColor(leftColor);
mRightView.exchangeColor(middleColor);
mLeftView.exchangeColor(rightColor);
expendAnimation();
}
});
set.start();
}
public CircleView getCircleView(Context context) {
CircleView circleView = new CircleView(context);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(dip2px(10),dip2px(10));
params.addRule(CENTER_IN_PARENT);
circleView.setLayoutParams(params);
return circleView;
}
private int dip2px(int dip) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dip,getResources().getDisplayMetrics());
}
@Override
public void setVisibility(int visibility) {
super.setVisibility(View.INVISIBLE);// 不要再去排放和计算,少走一些系统的源码(View的绘制流程)
// 清理动画
mLeftView.clearAnimation();
mRightView.clearAnimation();
// 把LoadingView从父布局移除
ViewGroup parent = (ViewGroup) getParent();
if (parent != null) {
parent.removeView(this);// 从父布局移除
removeAllViews();// 移除自己所有的View
}
mIsStopAnimator = true;
}
}
3.总结
在自定义 View/ViewGroup 时,如果内部有进行动画的效果处理,要注意内存的优化,往往需要如上面 setVisibility() 方法中的处理方式。