PathMeasure詳解文章:
[eclipse_xu]:http://www.reibang.com/p/3efa5341abcc
[GcsSloop]自定義View魔法系列:http://www.gcssloop.com/customview/Path_PathMeasure/
[扔物線]開發(fā)進(jìn)階系列:https://zhuanlan.zhihu.com/p/27787919
運用demo來自[Anonymous___]:http://www.reibang.com/p/4bb16cefca23
實踐+注解
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.Message;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import com.mps.hencoder.R;
/**
* 路徑繪制: 主要使用PathMeasure類
* Created by F011512088 on 2017/12/4.
*/
public class GranzortView extends View {
private final String TAG = GranzortView.class.getSimpleName();
private Paint paint;
private float strokeWidth = 10;
private float differ = 20;
private float maxRadius;
private float mViewWidth;
private float mViewHeight;
private Path innerCircle;//內(nèi)圓 path
private Path outerCircle;//外圓 path
private Path trangle1;//第一個三角形的 Path
private Path trangle2;//第二個三角形的 Path
private Path drawPath;//用于截取路徑的 Path,用于保存PathMeasure路徑
private float distance = 0;//當(dāng)前動畫執(zhí)行的百分比取值為0-1
private int duration = 3000;
private ValueAnimator valueAnimator;
private ValueAnimator.AnimatorUpdateListener animatorUpdateListener;
private ValueAnimator.AnimatorListener animatorListener;
private PathMeasure pathMeasure;
private State mCurrentState = State.CIRCLE_STATE;
private Handler mHanlder;
//三個階段的枚舉
private enum State {
CIRCLE_STATE,
TRANGLE_STATE,
FINISH_STATE
}
public GranzortView(Context context) {
super(context);
init();
}
public GranzortView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public GranzortView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
initPaint();
initPath();
initAnimatorListener();
initAnimator();
initHandler();
mCurrentState = State.CIRCLE_STATE;
valueAnimator.start();
}
private void initPath() {
innerCircle = new Path();
outerCircle = new Path();
trangle1 = new Path();
trangle2 = new Path();
drawPath = new Path();
pathMeasure = new PathMeasure();
}
private void initPaint() {
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(strokeWidth);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeJoin(Paint.Join.BEVEL);
paint.setShadowLayer(15, 0, 0, Color.WHITE);//白色光影效果
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mViewWidth = getMeasuredWidth();
mViewHeight = getMeasuredHeight();
setViewPaint();
}
private void setViewPaint() {
float minLength = Math.min(mViewWidth / 2, mViewHeight / 2);
maxRadius = (minLength - strokeWidth);
//畫布原點轉(zhuǎn)為中心點
RectF innerRect = new RectF(-maxRadius + differ, -maxRadius + differ, maxRadius - differ, maxRadius - differ);
RectF outerRect = new RectF(-maxRadius, -maxRadius, maxRadius, maxRadius);
innerCircle.addArc(innerRect, 150, -359.9F); // 不能取360f兄纺,否則可能造成測量到的值不準(zhǔn)確
outerCircle.addArc(outerRect, 60, -359.9F);
pathMeasure.setPath(innerCircle, false);
/**
* boolean getPosTan (float distance, float[] pos, float[] tan)
* 用于獲取路徑上某點的坐標(biāo)及其切線的坐標(biāo)
* 描述: 通過指定distance(0<distance<getLength),來獲取坐標(biāo)點和切線的坐標(biāo)颤芬,并保存到pos[]和tan[]數(shù)組中
*/
float[] pos = new float[2];
pathMeasure.getPosTan(0, pos, null); // 獲取開始位置的坐標(biāo)
trangle1.moveTo(pos[0], pos[1]);
pathMeasure.getPosTan((1f / 3f) * pathMeasure.getLength(), pos, null);
Log.e(TAG, "pos : " + pos[0] + " " + pos[1]);
trangle1.lineTo(pos[0], pos[1]);
pathMeasure.getPosTan((2f / 3f) * pathMeasure.getLength(), pos, null);
trangle1.lineTo(pos[0], pos[1]);
trangle1.close();
pathMeasure.getPosTan((2f / 3f) * pathMeasure.getLength(), pos, null);
Matrix matrix = new Matrix();
matrix.postRotate(-180);
trangle1.transform(matrix, trangle2);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(getResources().getColor(R.color.colorPrimary));
canvas.save();
canvas.translate(mViewWidth / 2, mViewHeight / 2);
switch (mCurrentState) {
case CIRCLE_STATE:
drawCircle(canvas);
break;
case TRANGLE_STATE:
drawLine(canvas);
break;
case FINISH_STATE:
drawDistanceLine(canvas);
break;
}
}
private void drawDistanceLine(Canvas canvas) {
canvas.drawPath(innerCircle, paint);
canvas.drawPath(outerCircle, paint);
drawPath.reset();
pathMeasure.setPath(trangle1, false);
pathMeasure.getSegment(0, distance * pathMeasure.getLength(), drawPath, true);
canvas.drawPath(drawPath, paint);
drawPath.reset();
pathMeasure.setPath(trangle2, false);
pathMeasure.getSegment(0, distance * pathMeasure.getLength(), drawPath, true);
canvas.drawPath(drawPath, paint);
}
private void drawLine(Canvas canvas) {
canvas.drawPath(innerCircle, paint);
canvas.drawPath(outerCircle, paint);
drawPath.reset();
pathMeasure.setPath(trangle1, 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(trangle2, false);
pathMeasure.getSegment(startD, stopD, drawPath, true);
canvas.drawPath(drawPath, paint);
}
private void drawCircle(Canvas canvas) {
//情況路徑,只記錄最后狀態(tài)
drawPath.reset();
pathMeasure.setPath(innerCircle, false);
/**
* boolean getSegment (float startD, float stopD, Path dst, boolean startWithMoveTo)
* 這個API用于截取整個Path的片段,通過參數(shù)startD和stopD來控制截取的長度晶通,
* 并將截取的Path保存到dst中,最后一個參數(shù)startWithMoveTo表示起始點是否使用moveTo方法遮斥,
* 通常為True济榨,保證每次截取的Path片段都是正常的鹊杖、完整的
*備注:
* 由于硬件加速的問題,PathMeasure中的getSegment在講Path添加到dst數(shù)組中時會被導(dǎo)致一些錯誤扛芽,需要通過mDst.lineTo(0,0)來避免這樣一個Bug
*/
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);
}
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) {
Log.e("star:", mCurrentState + "_");
}
@Override
public void onAnimationEnd(Animator animation) {
Log.e("end:", mCurrentState + "_");
mHanlder.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 initHandler() {
mHanlder = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (mCurrentState) {
case CIRCLE_STATE:
mCurrentState = State.TRANGLE_STATE;
valueAnimator.start();
break;
case TRANGLE_STATE:
mCurrentState = State.FINISH_STATE;
valueAnimator.start();
break;
}
}
};
}
/**
* 消費完成骂蓖,取消監(jiān)聽
*/
@Override
protected void onDetachedFromWindow() {
valueAnimator.removeAllListeners();
valueAnimator.cancel();
mHanlder = null;
super.onDetachedFromWindow();
}
}