美文网首页
PathMeasure相关知识及运用

PathMeasure相关知识及运用

作者: 攻城狮Song | 来源:发表于2017-12-04 14:22 被阅读26次

    PathMeasure详解文章:
    [eclipse_xu]:http://www.jianshu.com/p/3efa5341abcc
    [GcsSloop]自定义View魔法系列:http://www.gcssloop.com/customview/Path_PathMeasure/
    [扔物线]开发进阶系列:https://zhuanlan.zhihu.com/p/27787919

    运用demo来自[Anonymous___]:http://www.jianshu.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;//内圆 path
        private Path outerCircle;//外圆 path
        private Path trangle1;//第一个三角形的 Path
        private Path trangle2;//第二个三角形的 Path
        private Path drawPath;//用于截取路径的 Path,用于保存PathMeasure路径
        private float distance = 0;//当前动画执行的百分比取值为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);
            //画布原点转为中心点
            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,否则可能造成测量到的值不准确
            outerCircle.addArc(outerRect, 60, -359.9F);
    
            pathMeasure.setPath(innerCircle, false);
            /**
             * boolean getPosTan (float distance, float[] pos, float[] tan)
             * 用于获取路径上某点的坐标及其切线的坐标
             * 描述: 通过指定distance(0<distance<getLength),来获取坐标点和切线的坐标,并保存到pos[]和tan[]数组中
             */
            float[] pos = new float[2];
            pathMeasure.getPosTan(0, pos, null);        // 获取开始位置的坐标
            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) {
            //情况路径,只记录最后状态
            drawPath.reset();
            pathMeasure.setPath(innerCircle, false);
            /**
             * boolean getSegment (float startD, float stopD, Path dst, boolean startWithMoveTo)
             * 这个API用于截取整个Path的片段,通过参数startD和stopD来控制截取的长度,
             * 并将截取的Path保存到dst中,最后一个参数startWithMoveTo表示起始点是否使用moveTo方法,
             * 通常为True,保证每次截取的Path片段都是正常的、完整的
             *备注:
             * 由于硬件加速的问题,PathMeasure中的getSegment在讲Path添加到dst数组中时会被导致一些错误,需要通过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;
                    }
                }
            };
        }
    
        /**
         * 消费完成,取消监听
         */
        @Override
        protected void onDetachedFromWindow() {
            valueAnimator.removeAllListeners();
            valueAnimator.cancel();
            mHanlder = null;
            super.onDetachedFromWindow();
        }
    }
    

    相关文章

      网友评论

          本文标题:PathMeasure相关知识及运用

          本文链接:https://www.haomeiwen.com/subject/tkcxixtx.html