依然是個收藏已久的Gif,今天來實現一下。
記憶裡好像是有人已經實現過了。剛才去找了下,又沒找到。如果哪個朋友看到過過,給我發下,我來對比下,我想總會又收穫的。Gif如下圖。
效果圖如下:
原理
原理也不復雜。就是用Path 定義個圓型的軌跡。不停的獲取該圓形的軌跡的xy座標。
黑色圓 執行在該path 的上半圓。空心圓執行在該Path的下半圓。
如 圖如下:
初始化
根據個資料準備好圓形。
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);
}
複製程式碼
旋轉圓的Path
測量完成,也就是說所有的資料已經準備好,然後在該方法中初始化 mNumber-1 個旋轉圓的Path
@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度的位置。
黑色圓-回: 同理。
//黑色圓-去:
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();
}
}
複製程式碼