效果图:
直接上代码啦:
package com.example.administrator.myapplication.customview;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.drawable.GradientDrawable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import com.example.administrator.myapplication.R;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Administrator on 2016/9/14.
*/
public class AnimationView extends RelativeLayout {
private final int SMALL_CIRCLE_RADIS = dp2px(20);
private final int LARGE_CIRCLE_RADIS = dp2px(25);
private final int OUT_CIRCLE_RADIS = dp2px(50);
private final int TYPE_SMALL = 0x01;
private final int TYPE_LARGE = 0x02;
private final int TYPE_OUTER = 0x03;
private Context mContext;
//小圆圈颜色
private int mSmallCircleColor;
//大圆圈颜色
private int mLargeCircleColor;
//外圆圈颜色
private int mOutCircleColor;
//动画
private AnimatorSet mAimationSet;
private PropertyValuesHolder mValueScaleX;
private PropertyValuesHolder mValueScaleY;
private PropertyValuesHolder mValuesScale;
//小圆移动路径
private Path mCirclePath;
private LinearLayout mCircleContainer;
private CircleView mOutCircleView;
private CircleView mSmallCircleView;
private CircleView mLargeCircleView;
private List<CircleView> mCircleViews;
//当前默认的位置
private int mFocusPosition;
private int mCount;
public AnimationView(Context context) {
this(context, null);
mContext = context;
}
public AnimationView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
mContext = context;
}
public AnimationView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.AnimationView);
mSmallCircleColor = ta.getColor(R.styleable.AnimationView_smallCircleColor, getResources().getColor(R.color.colorAccent));
mLargeCircleColor = ta.getColor(R.styleable.AnimationView_largeCircleColor, getResources().getColor(R.color.colorPrimary));
mOutCircleColor = ta.getColor(R.styleable.AnimationView_outCircleColor, getResources().getColor(android.R.color.holo_orange_light));
ta.recycle();
//初始化布局
View rootView = LayoutInflater.from(getContext()).inflate(R.layout.bg_animationview, this);
mCircleContainer = (LinearLayout) rootView.findViewById(R.id.ll_container);
mOutCircleView = (CircleView) rootView.findViewById(R.id.out_circle2);
mOutCircleView.setRadis(OUT_CIRCLE_RADIS);
//设置背景大圆颜色
View containerWrapper = rootView.findViewById(R.id.wrapper);
mOutCircleView.setColor(mOutCircleColor);
GradientDrawable shape = (GradientDrawable) containerWrapper.getBackground();
shape.setColor(mOutCircleColor);
mValueScaleX = PropertyValuesHolder.ofFloat("scaleX", 1, 0.8f, 1);
mValueScaleY = PropertyValuesHolder.ofFloat("scaleY", 1, 0.8f, 1);
mValuesScale = PropertyValuesHolder.ofFloat("scaleY", 1, 0.5f, 1);
mCirclePath = new Path();
mCircleViews = new ArrayList<CircleView>();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
mLargeCircleView = mCircleViews.get(mFocusPosition);
mOutCircleView.setCenter(mLargeCircleView.getCenter());
}
public void setCount(int count, int focus) {
if (count < 2) {
return;
}
if (focus > count || focus < 0) {
return;
}
mFocusPosition = focus;
mCount = count;
/** 1:创建圆
* 2:往linearlayout动态添加圆
* */
mCircleContainer.removeAllViews();
mCircleViews.clear();
//左边放小圆
for (int i = 0; i < focus; i++) {
addSmallCircle();
}
//中间大圆
addLargeCircle();
//右边小圆
for (int i = focus + 1; i < count; i++) {
addSmallCircle();
}
}
private void addLargeCircle() {
CircleView largeCircle = CreateCircle(TYPE_LARGE);
mCircleViews.add(largeCircle);
mCircleContainer.addView(largeCircle);
}
private void addSmallCircle() {
CircleView smallCircle = CreateCircle(TYPE_SMALL);
mCircleViews.add(smallCircle);
mCircleContainer.addView(smallCircle);
}
public void moveRight() {
if (mAimationSet != null && mAimationSet.isRunning()) {
return;
}
move(true);
}
public void moveLeft() {
if (mAimationSet != null && mAimationSet.isRunning()) {
return;
}
move(false);
}
private void move(boolean toRight) {
mLargeCircleView = mCircleViews.get(mFocusPosition);
final int nextPos = getNextPosition(toRight);
if (nextPos < 0) {
return;
}
mSmallCircleView = mCircleViews.get(nextPos);
//计算移动
float largeCircleX = toRight ? mSmallCircleView.getX() - (mLargeCircleView.getWidth() - mSmallCircleView.getWidth()) : mSmallCircleView.getX();
float smallCircleX = toRight ? mLargeCircleView.getX() : mLargeCircleView.getX() + (mLargeCircleView.getWidth() - mSmallCircleView.getWidth());
float outCircleX = toRight ? mSmallCircleView.getX() - (mOutCircleView.getWidth() - mSmallCircleView.getWidth()) : mSmallCircleView.getX();
//l argeCircleView动画 X平移 XY缩放
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("X", mLargeCircleView.getX(), largeCircleX);
ObjectAnimator largeCircleAnim = ObjectAnimator.ofPropertyValuesHolder(mLargeCircleView, pvhX, mValueScaleX, mValueScaleY);
//2 outCircleView动画 X平移 XY缩放
pvhX = PropertyValuesHolder.ofFloat("X", mOutCircleView.getX(), outCircleX);
ObjectAnimator outCircleAnim = ObjectAnimator.ofPropertyValuesHolder(mOutCircleView, pvhX, mValueScaleX, mValueScaleY);
//3 smallCircleView动画 XY缩放 Path移动
PointF center = mSmallCircleView.getCenter();
PointF endCenter = new PointF(center.x - (mSmallCircleView.getX() - smallCircleX), center.y);
mCirclePath.reset();
mCirclePath.moveTo(center.x, center.y);
//贝塞尔曲线
mCirclePath.quadTo((center.x + endCenter.x) / 2, (center.y + endCenter.y) / 2 + 1.5f * dp2px(30), endCenter.x, endCenter.y);
ObjectAnimator smallCircleAnim = ObjectAnimator.ofObject(mSmallCircleView, "Center", null, mCirclePath);
ObjectAnimator smallCircleAnim2 = ObjectAnimator.ofPropertyValuesHolder(mSmallCircleView, mValuesScale);
mAimationSet = new AnimatorSet();
mAimationSet.play(largeCircleAnim).with(outCircleAnim).with(smallCircleAnim).with(smallCircleAnim2);
mAimationSet.setInterpolator(new AccelerateDecelerateInterpolator());
mAimationSet.setDuration(1000);
mAimationSet.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
swapCircle(mFocusPosition, nextPos);
mFocusPosition = nextPos;
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
mAimationSet.start();
}
void swapCircle(int position1, int position2) {
CircleView tmp = mCircleViews.get(position1);
mCircleViews.set(position1, mCircleViews.get(position2));
mCircleViews.set(position2, tmp);
}
private int getNextPosition(boolean toRight) {
int next = mFocusPosition;
if (toRight) {
next = next + 1;
} else {
next = next - 1;
}
if (next < 0 || next >= mCount) {
next = -1;
}
return next;
}
private CircleView CreateCircle(int type) {
CircleView circle = new CircleView(mContext);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.weight = 1;
params.gravity = Gravity.CENTER_VERTICAL;
switch (type) {
case TYPE_SMALL:
params.height = params.width = SMALL_CIRCLE_RADIS * 2;
circle.setColor(mSmallCircleColor);
circle.setRadis(SMALL_CIRCLE_RADIS);
break;
case TYPE_LARGE:
params.height = params.width = LARGE_CIRCLE_RADIS * 2;
circle.setColor(mLargeCircleColor);
circle.setRadis(LARGE_CIRCLE_RADIS);
break;
case TYPE_OUTER:
params.height = params.width = OUT_CIRCLE_RADIS * 2;
circle.setColor(mOutCircleColor);
circle.setRadis(OUT_CIRCLE_RADIS);
break;
default:
break;
}
circle.setLayoutParams(params);
return circle;
}
private int dp2px(int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
}
private int sp2px(int sp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics());
}
}
package com.example.administrator.myapplication.customview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by Administrator on 2016/9/14.
*/
public class CircleView extends View {
private Context mContext;
private int mCircleColor;
private float mCircleRadis;
private float mCenterX;
private float mCenterY;
private int mWidth;
private int mHeight;
private Paint mCirclePaint;
public CircleView(Context context) {
this(context, null);
mContext = context;
}
public CircleView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
mContext = context;
}
public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
initPaint();
}
private void initPaint() {
mCirclePaint = new Paint();
mCirclePaint.setAntiAlias(true);
mCirclePaint.setStyle(Paint.Style.FILL_AND_STROKE);
mCirclePaint.setStrokeWidth(1);
mCirclePaint.setColor(mCircleColor);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
mCenterX = (right - left) / 2;
mCenterY = (bottom - top) / 2;
mCircleRadis = Math.min(mCenterX, mCenterY);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(mCenterX, mCenterY, mCircleRadis - 5, mCirclePaint);
}
public void setColor(int color) {
mCirclePaint.setColor(color);
invalidate();
}
public void setRadis(float radis) {
mCircleRadis = radis;
invalidate();
}
//那中心点
public PointF getCenter() {
return new PointF(getX() + getWidth() / 2, getY() + getHeight() / 2);
}
public void setCenter(float cx, float cy) {
setX(cx - getWidth() / 2);
setY(cy - getHeight() / 2);
}
public void setCenter(PointF point) {
setCenter(point.x, point.y);
postInvalidate();
}
public float getRadis() {
return mCircleRadis;
}
}