模拟器GIF效果
步骤
分析:
内外圆环
内部五角星 = 两个三角形
- 动效,属性动画ValueAnimator : 0-1 通过getAnimatedValue()获取值并刷新
- 实时截取path 并绘制onDraw: pathMeasure.getSegment
自定义view 五角星阵
package com.gestwr.view;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.RectF;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import androidx.annotation.Nullable;
public class GranzortView extends View {
private Paint paint;
private Path innerCircle;//内圆 path
private Path outerCircle;//外圆 path
private Path triangle1;//第一个三角形的 Path
private Path triangle2;//第二个三角形的 Path
private Path drawPath;//用于截取路径的 Path
private PathMeasure pathMeasure;
private float mViewWidth;
private float mViewHeight;
private long duration = 3000;
private ValueAnimator valueAnimator;
private Handler mHandler;
private float distance;//当前动画执行的百分比取值为0-1
private ValueAnimator.AnimatorUpdateListener animatorUpdateListener;
private Animator.AnimatorListener animatorListener;
private State mCurrentState = State.CIRCLE_STATE;
//三个阶段的枚举
private enum State {
CIRCLE_STATE,
TRIGONAL_STATE,
FINISH_STATE
}
public GranzortView(Context context) {
this(context, null);
}
public GranzortView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public GranzortView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mViewWidth = w;
mViewHeight = h;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.parseColor("#FFBB86FC"));
canvas.save();
canvas.translate(mViewWidth / 2, mViewHeight / 2);
switch (mCurrentState) {
case CIRCLE_STATE:
drawPath.reset();
pathMeasure.setPath(innerCircle, false);
pathMeasure.getSegment(0, distance * pathMeasure.getLength(), drawPath, true);
canvas.drawPath(drawPath, paint);
pathMeasure.setPath(outerCircle, false);
drawPath.reset();
pathMeasure.getSegment(0, distance * pathMeasure.getLength(), drawPath, true);
canvas.drawPath(drawPath, paint);
break;
case TRIGONAL_STATE:
canvas.drawPath(innerCircle, paint);
canvas.drawPath(outerCircle, paint);
drawPath.reset();
pathMeasure.setPath(triangle1, false);
float stopD = distance * pathMeasure.getLength();
float startD = stopD - (0.5f - Math.abs(0.5f - distance)) * 200;
pathMeasure.getSegment(startD, stopD, drawPath, true);
canvas.drawPath(drawPath, paint);
drawPath.reset();
pathMeasure.setPath(triangle2, false);
pathMeasure.getSegment(startD, stopD, drawPath, true);
canvas.drawPath(drawPath, paint);
break;
case FINISH_STATE:
canvas.drawPath(innerCircle, paint);
canvas.drawPath(outerCircle, paint);
drawPath.reset();
pathMeasure.setPath(triangle1, false);
pathMeasure.getSegment(0, distance * pathMeasure.getLength(), drawPath, true);
canvas.drawPath(drawPath, paint);
drawPath.reset();
pathMeasure.setPath(triangle2, false);
pathMeasure.getSegment(0, distance * pathMeasure.getLength(), drawPath, true);
canvas.drawPath(drawPath, paint);
break;
default:
break;
}
canvas.restore();
}
private void init() {
initPaint();
initPath();
initHandler();
initAnimatorListener();
initAnimator();
mCurrentState = State.CIRCLE_STATE;
valueAnimator.start();
}
private void initHandler() {
mHandler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(Message msg) {
switch (mCurrentState) {
case CIRCLE_STATE:
mCurrentState = State.TRIGONAL_STATE;
valueAnimator.start();
break;
case TRIGONAL_STATE:
mCurrentState = State.FINISH_STATE;
valueAnimator.start();
break;
default:
break;
}
}
};
}
private void initAnimatorListener() {
animatorUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
distance = (float) animation.getAnimatedValue();
invalidate();
}
};
animatorListener = new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
mHandler.sendEmptyMessage(0);
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
};
}
private void initAnimator() {
valueAnimator = ValueAnimator.ofFloat(0, 1).setDuration(duration);
valueAnimator.addUpdateListener(animatorUpdateListener);
valueAnimator.addListener(animatorListener);
}
private void initPath() {
innerCircle = new Path();
outerCircle = new Path();
triangle1 = new Path();
triangle2 = new Path();
drawPath = new Path();
pathMeasure = new PathMeasure();
RectF innerRect = new RectF(-220, -220, 220, 220);
RectF outerRect = new RectF(-280, -280, 280, 280);
innerCircle.addArc(innerRect, 150, -359.9F); // 不能取360f,否则可能造成测量到的值不准确
outerCircle.addArc(outerRect, 60, -359.9F);
pathMeasure.setPath(innerCircle, false);
float[] pos = new float[2];
pathMeasure.getPosTan(0, pos, null); // 获取开始位置的坐标
triangle1.moveTo(pos[0], pos[1]);
pathMeasure.getPosTan((1f / 3f) * pathMeasure.getLength(), pos, null);
Log.i("GranzortView", "pos : " + pos[0] + " " + pos[1]);
triangle1.lineTo(pos[0], pos[1]);
pathMeasure.getPosTan((2f / 3f) * pathMeasure.getLength(), pos, null);
triangle1.lineTo(pos[0], pos[1]);
triangle1.close();
pathMeasure.getPosTan((2f / 3f) * pathMeasure.getLength(), pos, null);
Matrix matrix = new Matrix();
matrix.postRotate(-180);
triangle1.transform(matrix, triangle2);
}
private void initPaint() {
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(10);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeJoin(Paint.Join.BEVEL);
paint.setShadowLayer(15, 0, 0, Color.WHITE);//白色光影效果
}
}
转载:https://juejin.cn/post/6844903492402806797