依然是个收藏已久的Gif,今天来实现一下。canvas
记忆里好像是有人已经实现过了。刚才去找了下,又没找到。若是哪一个朋友看到过过,给我发下,我来对比下,我想总会又收获的。Gif以下图。bash
效果图以下: ide
原理也不复杂。就是用Path 定义个圆型的轨迹。不停的获取该圆形的轨迹的xy坐标。post
黑色圆 运行在该path 的上半圆。空心圆运行在该Path的下半圆。ui
如 图以下:this
根据个数据准备好圆形。spa
public class AlterCircleView extends BaseView {
private int mNumber = 5;
//--------------------------------------------------------------
// 圆 属性
private int mRadius = DisplayUtils.dp2px(this.getContext(), 10);
// 交替的两个圆
private Circle mCurrCircle, mNextCircle;
private Circle[] mCircles = new Circle[mNumber];
private List<Path> mPaths = new ArrayList<>();
//--------------------------------------------------------------
// 测量 围绕的中心圆 属性
float distance = 0.0f;
float pos[] = new float[2];
float tan[] = new float[2];
// 测量圆的measure
private PathMeasure measure;
// 指定当前第几个 圆Path 总数为 number-1
private int index = 0;
//--------------------------------------------------------------
// 黑色圆形, 去为 false ,回来 为 true
private boolean mBack = false;
public AlterCircleView(Context context) {
this(context, null);
}
public AlterCircleView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public AlterCircleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
List<Circle> list = new CircleFactory(mRadius).generateCircles(mNumber);
for (int i = 0; i < list.size(); i++) {
mCircles[i] = list.get(i);
}
mCurrCircle = mCircles[0]; // 黑色圆
mNextCircle = mCircles[1];
measure = new PathMeasure();
}
复制代码
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int measuredWidth = width, measuredHeight = height;
if (widthMode != MeasureSpec.EXACTLY) {
measuredWidth = mRadius * 2 * mNumber + mRadius * (mNumber - 1) + mMargin * 2 + mPadding * 2;
}
if (heightMode != MeasureSpec.EXACTLY) {
// 当两个圆,交替到 垂直状态,+ 2个半径 也就是 最大的高度。
int r = mNextCircle.getX() - mCurrCircle.getX();
measuredHeight = r + mRadius * 2 + mMargin + mPadding;
}
setMeasuredDimension(measuredWidth, measuredHeight);
}
复制代码
测量完成,也就是说全部的数据已经准备好,而后在该方法中初始化 mNumber-1 个旋转圆的Path3d
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// 两个圆形 中间的 旋转圆,Path 路径
for (int i = 0; i < mCircles.length - 1; i++) {
Circle currCircle = mCircles[i];
Circle nextCircle = mCircles[i + 1];
int r = (nextCircle.getX() + currCircle.getX()) / 2;
int radius = r - currCircle.getX();
Path path = new Path();
path.addCircle(r, 0, radius, Path.Direction.CW);
mPaths.add(path);
}
}
复制代码
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(mMargin + mPadding + mRadius, mViewHeight / 2);
canvas.save();
for (Circle c : mCircles) {
boolean solid = c.getSolid();
mPaint.setStyle(Paint.Style.STROKE);
if (solid)
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.drawCircle(c.getX(), c.getY(), c.getRadius(), mPaint);
}
// index 来 设置 要 测量的path
measure.setPath(mPaths.get(index), false);
// 获取当前 点的 xy坐标,以及正切(此处没用)
measure.getPosTan(distance, pos, tan);
mNextCircle.setX((int) pos[0]);
mNextCircle.setY((int) pos[1]);
复制代码
黑色圆-去: distance 初始化值为0.位置为圆心右手边。
mNextCircle圆形跟随该路径移动至180度位置。
而后,黑色圆形则从180度的位置+distance开始移动至360/0度的位置。rest
黑色圆-回: 同理。code
//黑色圆-去:
if (!mBack) {
if (distance < measure.getLength() / 2) {
float f = measure.getLength() / 2 + distance;
float pos[] = new float[2];
float tan[] = new float[2];
measure.getPosTan(f, pos, tan);
mCurrCircle.setX((int) pos[0]);
mCurrCircle.setY((int) pos[1]);
distance += 2;
} else {
// 交换2个圆位置
Circle temp = mCircles[index];
mCircles[index] = mCircles[index + 1];
mCircles[index + 1] = temp;
if (index < mPaths.size() - 1) {
index++;
distance = 0;
mCurrCircle = mCircles[index];
mNextCircle = mCircles[index + 1];
} else {
distance = measure.getLength() / 2;
mBack = true;
}
}
}
//黑色圆-回:
else {
if (distance < measure.getLength()) {
float f = distance - measure.getLength() / 2;
float pos[] = new float[2];
float tan[] = new float[2];
measure.getPosTan(f, pos, tan);
mCurrCircle.setX((int) pos[0]);
mCurrCircle.setY((int) pos[1]);
distance += 2;
} else {
Circle temp = mCircles[index];
mCircles[index] = mCircles[index + 1];
mCircles[index + 1] = temp;
if (index > 0) {
index--;
distance = measure.getLength() / 2;
mCurrCircle = mCircles[index + 1];
mNextCircle = mCircles[index];
} else {
distance = 0;
mBack = false;
}
}
}
postInvalidate();
canvas.restore();
}
}
复制代码