美文网首页
动画2-path动画

动画2-path动画

作者: niudeyang | 来源:发表于2019-01-03 15:53 被阅读10次

    path动画有两种 1 view按照指定的路径运动 2动态绘path

    ps:1需要用到获取控件位置的方法,2需要用到path相关知识(参考文章“”绘制技巧”中view基础知识)
    获取控件的定位

    /**
         * 获取控件位置的两种方法
         */
        private void location() {
    
            int[] startLocation = new int[2];
            iv.getLocationInWindow(startLocation);
            tvs.setText("left="+startLocation[0]+"top="+startLocation[1]);//包含了satusbar的高度
    
            iv.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                 @Override
                 public void onGlobalLayout() {
                     // 获取减少图标的位置
                        tvs.setText("left="+iv.getLeft()+"top="+iv.getTop()+"right="+iv.getRight()+"bottom="+iv.getBottom());//以activity根布局左和上为参考
    
                     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                         iv.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                     }
                 }
             });
        }
    

    1添加购物车动画 文章来源(链接:https://www.jianshu.com/p/b7bacc6805c2

    <RelativeLayout
            android:id="@+id/ll"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:orientation="vertical"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintTop_toBottomOf="@id/iv">
    
            <TextView
                android:id="@+id/tv_add"
                android:layout_width="40dp"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:background="@color/red"
                android:text="+"
                android:textColor="@color/white"
                android:textSize="20sp" />
    
            <TextView
                android:id="@+id/tv_cart"
                android:layout_width="60dp"
                android:layout_height="50dp"
               android:layout_alignParentBottom="true"
                android:background="@color/blue"
                android:text="我是购物车" />
        </RelativeLayout>
    
    
     private void startpathAnimation() {
            final ImageView goods = new ImageView(this);
            goods.setImageResource(R.mipmap.ic_launcher_round);
            ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            goods.setLayoutParams(lp);
            mroot.addView(goods);
            // 控制点的位置
            int[] recyclerLocation = new int[2];
            mroot.getLocationInWindow(recyclerLocation);
            // 加入点的位置起始点
            int[] startLocation = new int[2];
            tvadd.getLocationInWindow(startLocation);
            // 购物车的位置终点
            int[] endLocation = new int[2];
            tvcard.getLocationInWindow(endLocation);
           // tvs.setText("rx"+recyclerLocation[0]+"ry"+recyclerLocation[1]+"sx="+startLocation[0]+"sy="+startLocation[1]+"ex="+endLocation[0]+"ey="+endLocation[1]);
    
            // TODO:  考虑到状态栏的问题,不然会往下偏移状态栏的高度
            int startX = startLocation[0] - recyclerLocation[0];
            int startY = startLocation[1] - recyclerLocation[1];
            // TODO:  和上面一样
            int endX = endLocation[0] - recyclerLocation[0];
            int endY = endLocation[1] - recyclerLocation[1];
    
            tvs.setText("sx="+startX+"sy="+startY+"ex="+endX+"ey="+endY);
    
            Path path=new Path();
            path.moveTo(startX,startY);
            // 使用二阶贝塞尔曲线:注意第一个起始坐标越大,贝塞尔曲线的横向距离就会越大,一般按照下面的式子取即可
            path.quadTo((startX + endX) / 2,startY, endX, endY);
            // mPathMeasure用来计算贝塞尔曲线的曲线长度和贝塞尔曲线中间插值的坐标,如果是true,path会形成一个闭环
            final PathMeasure pathMeasure = new PathMeasure(path, false);
            // 属性动画实现(从0到贝塞尔曲线的长度之间进行插值计算,获取中间过程的距离值)
            ValueAnimator valueAnimator=new ValueAnimator().ofFloat(0,pathMeasure.getLength());
            //根据距离计算动画时间
            int tempx=Math.abs(startX-endX);
            int tempy=Math.abs(startY-endY);
    
            int time= (int) (0.3*Math.sqrt(tempx*tempx+tempy*tempy));
    
            valueAnimator.setDuration(time);
            valueAnimator.start();
            valueAnimator.setInterpolator(new AccelerateInterpolator());
            final float[] mCurrentPosition=new float[2];
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float value= (float) animation.getAnimatedValue();
                     pathMeasure.getPosTan(value,mCurrentPosition,null);
                     goods.setTranslationX(mCurrentPosition[0]);
                     goods.setTranslationY(mCurrentPosition[1]);
                }
            });
    
            valueAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    mroot.removeView(goods);//动画结束后删除做动画的控件
                }
            });
    

    2购物车升级版,按照指定的路径运动

    捕获.PNG
    /**
    绘制路径便于观察
    */
    public class PathView extends View {
    
        private Paint paint;
    
        public PathView(Context context, AttributeSet attrs) {
            super(context, attrs);
            initView();
        }
    
        private void initView() {
            paint = new Paint();
            //抗锯齿
            paint.setAntiAlias(true);
            //防抖动
            paint.setDither(true);
            //设置画笔未实心
            paint.setStyle(Paint.Style.STROKE);
            //设置颜色
            paint.setColor(Color.GREEN);
            //设置画笔宽度
            paint.setStrokeWidth(3);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            Path path = new Path();
            path.moveTo(60,60);
            path.lineTo(460,460);
            path.quadTo(660, 260, 860, 460); //订单
            path.cubicTo(160,660,960,1060,260,1260);
            canvas.drawPath(path,paint);
        }
    }
    
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.example.eric.constrantapplication.Main2Activity">
      <com.example.eric.constrantapplication.view.PathView
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          />
      <ImageView
          android:id="@+id/iv"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:src="@mipmap/ic_launcher_round"
          />
    </android.support.constraint.ConstraintLayout>
    
    

    在activity中

    private void pathAnimate() {
            Path path = new Path();
            path.moveTo(60,60);
            path.lineTo(460,460);
            path.quadTo(660, 260, 860, 460);
            path.cubicTo(160,660,960,1060,260,1260);
            final PathMeasure pathMeasure = new PathMeasure(path, false);//是否闭合
            // 属性动画实现(从0到贝塞尔曲线的长度之间进行插值计算,获取中间过程的距离值)
            ValueAnimator valueAnimator=new ValueAnimator().ofFloat(0,pathMeasure.getLength());
            valueAnimator.setDuration(5000);
            valueAnimator.start();
            //valueAnimator.setInterpolator(new AccelerateInterpolator());
            final float[] mCurrentPosition=new float[2];
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float value= (float) animation.getAnimatedValue();
                    pathMeasure.getPosTan(value,mCurrentPosition,null);
                    iv.setTranslationX(mCurrentPosition[0]-60);
                    iv.setTranslationY(mCurrentPosition[1]-60);
                }
            });
        }
    

    3箭头绕圆环运动 箭头的方向与圆环一致(http://www.gcssloop.com/customview/Path_PathMeasure)

    3.PNG
    Path path = new Path();                                 // 创建 Path
    
    path.addCircle(0, 0, 200, Path.Direction.CW);           // 添加一个圆形
    
    PathMeasure measure = new PathMeasure(path, false);     // 创建 PathMeasure
    
    currentValue += 0.005;                                  // 计算当前的位置在总长度上的比例[0,1]
    if (currentValue >= 1) {
        currentValue = 0;
    }
    
    // 获取当前位置的坐标以及趋势的矩阵
    measure.getMatrix(measure.getLength() * currentValue, mMatrix, PathMeasure.TANGENT_MATRIX_FLAG | PathMeasure.POSITION_MATRIX_FLAG);
    
    mMatrix.preTranslate(-mBitmap.getWidth() / 2, -mBitmap.getHeight() / 2);   // <-- 将图片绘制中心调整到与当前点重合(注意:此处是前乘pre)
    
    canvas.drawPath(path, mDeafultPaint);                                   // 绘制 Path
    canvas.drawBitmap(mBitmap, mMatrix, mDeafultPaint);                     // 绘制箭头
    
    invalidate();              
    

    4支付宝支付成功 失败的动态效果 文章来源(https://www.jb51.net/article/99966.htm

    捕获.PNG

    首先我们需要了解PathMeasure这个类,这个类我们可以理解为用来管理Path。我们主要看几个方法。
    PathMeasure(): 构造方法 ,实例化一个对象
    PathMeasure(Path path,boolean isClosed):传入Path对象和是否闭合,path对象不能为空
    getLength():获取当前轮廓、外形的总长度, 如果没有设置Path对象,返回0
    getSegment(float startD,float stopD,Path dst,boolean startWithMoveTo):调用这个方法,我们可以获取到指定范围内的一段轮廓,存入到dst参数中。所以,这个方法传入的参数分别为长度起始值、结束值、装这一段路径的Path对象、是否MoveTo。另外,这个方法返回值为Boolean类型,如果getLength为0的话,返回false,或者startD > stopD,同样返回false。
    setPath(Path path , boolean isClosed):给当前PathMeasure对象设置Path
    nextContour():移动到下一个轮廓(path)

    package com.mintmedical.wavedemo;
     
    import android.animation.ValueAnimator;
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.Path;
    import android.graphics.PathMeasure;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.View;
     
    /**
     * Created by MooreLi on 2016/12/12.
     */
     
    public class ResultAnimation extends View implements ValueAnimator.AnimatorUpdateListener {
     private Context mContext;
     /**
      * paint对象
      */
     private Paint mPaint;
     /**
      * Path和对应的空Path用来填充
      */
     private Path mPathCircle;
     private Path mPathCircleDst;
     private Path mPathRight;
     private Path mPathRightDst;
     private Path mPathWrong1;
     private Path mPathWrong2;
     private Path mPathWrong1Dst;
     private Path mPathWrong2Dst;
     /**
      * Path管理
      */
     private PathMeasure mPathMeasure;
     /**
      * 动画
      */
     private ValueAnimator mCircleAnimator;
     private ValueAnimator mRightAnimator;
     private ValueAnimator mWrong1Animator;
     private ValueAnimator mWrong2Animator;
     /**
      * 当前绘制进度占总Path长度百分比
      */
     private float mCirclePercent;
     private float mRightPercent;
     private float mWrong1Percent;
     private float mWrong2Percent;
     /**
      * 线宽
      */
     private int mLineWidth;
     /**
      * 正确动画 错误动画
      */
     public static final int RESULT_RIGHT = 1;
     public static final int RESULT_WRONG = 2;
     /**
      * 当前结果类型
      */
     private int mResultType = RESULT_WRONG;
     
     public ResultAnimation(Context context) {
      super(context);
      mContext = context;
      init();
     }
     
     public ResultAnimation(Context context, AttributeSet attrs) {
      super(context, attrs);
      mContext = context;
      init();
     }
     
     public ResultAnimation(Context context, AttributeSet attrs, int defStyleAttr) {
      super(context, attrs, defStyleAttr);
      mContext = context;
      init();
     }
     
     private void init() {
      mLineWidth = dp2px(3);
      mPaint = new Paint();
      mPaint.setAntiAlias(true);
      mPaint.setStrokeWidth(mLineWidth);
      mPaint.setStyle(Paint.Style.STROKE);
      mPaint.setColor(Color.GREEN);
     
      initPath();
     }
     
     private void initPath() {
      mPathCircle = new Path();
      mPathCircleDst = new Path();
      mPathRight = new Path();
      mPathRightDst = new Path();
      mPathWrong1 = new Path();
      mPathWrong2 = new Path();
      mPathWrong1Dst = new Path();
      mPathWrong2Dst = new Path();
     
      mPathMeasure = new PathMeasure();
     
      //实例化对象
      mCircleAnimator = ValueAnimator.ofFloat(0, 1);
      //设置时长为1000ms
      mCircleAnimator.setDuration(1000);
      //开始动画
      mCircleAnimator.start();
      //设置动画监听
      mCircleAnimator.addUpdateListener(this);
     
      mRightAnimator = ValueAnimator.ofFloat(0, 1);
      mRightAnimator.setDuration(500);
      mRightAnimator.addUpdateListener(this);
     
      mWrong1Animator = ValueAnimator.ofFloat(0, 1);
      mWrong1Animator.setDuration(300);
      mWrong1Animator.addUpdateListener(this);
      mWrong2Animator = ValueAnimator.ofFloat(0, 1);
      mWrong2Animator.setDuration(300);
      mWrong2Animator.addUpdateListener(this);
     
     }
     
     @Override
     protected void onDraw(Canvas canvas) {
      super.onDraw(canvas);
      if (mResultType == RESULT_RIGHT) {
       mPaint.setColor(Color.GREEN);
      } else {
       mPaint.setColor(Color.RED);
      }
     
      //画圆
      mPathCircle.addCircle(getWidth() / 2, getWidth() / 2, getWidth() / 2 - mLineWidth, Path.Direction.CW);
      mPathMeasure.setPath(mPathCircle, false);
      mPathMeasure.getSegment(0, mCirclePercent * mPathMeasure.getLength(), mPathCircleDst, true);
      canvas.drawPath(mPathCircleDst, mPaint);
      if (mResultType == RESULT_RIGHT) {
       //画对勾
       mPathRight.moveTo(getWidth() / 4, getWidth() / 2);
       mPathRight.lineTo(getWidth() / 2, getWidth() / 4 * 3);
       mPathRight.lineTo(getWidth() / 4 * 3, getWidth() / 4);
       if (mCirclePercent == 1) {
        mPathMeasure.nextContour();
        mPathMeasure.setPath(mPathRight, false);
        mPathMeasure.getSegment(0, mRightPercent * mPathMeasure.getLength(), mPathRightDst, true);
        canvas.drawPath(mPathRightDst, mPaint);
       }
      } else {
       mPathWrong1.moveTo(getWidth() / 4 * 3, getWidth() / 4);
       mPathWrong1.lineTo(getWidth() / 4, getWidth() / 4 * 3);
     
       mPathWrong2.moveTo(getWidth() / 4, getWidth() / 4);
       mPathWrong2.lineTo(getWidth() / 4 * 3, getWidth() / 4 * 3);
     
       if (mCirclePercent == 1) {
        mPathMeasure.nextContour();
        mPathMeasure.setPath(mPathWrong1, false);
        mPathMeasure.getSegment(0, mWrong1Percent * mPathMeasure.getLength(), mPathWrong1Dst, true);
        canvas.drawPath(mPathWrong1Dst, mPaint);
       }
       if (mWrong1Percent == 1) {
        mPathMeasure.nextContour();
        mPathMeasure.setPath(mPathWrong2, false);
        mPathMeasure.getSegment(0, mWrong2Percent * mPathMeasure.getLength(), mPathWrong2Dst, true);
        canvas.drawPath(mPathWrong2Dst, mPaint);
       }
      }
     }
     
     private int dp2px(int dp) {
      float scale = mContext.getResources().getDisplayMetrics().density;
      return (int) (scale * dp + 0.5f);
     }
     
     @Override
     public void onAnimationUpdate(ValueAnimator animation) {
      //圆形动画
      if (animation.equals(mCircleAnimator)) {
       mCirclePercent = (float) animation.getAnimatedValue();
       invalidate();
       Log.e("TEST","percent:"+mCirclePercent);
       if (mCirclePercent == 1) {
        if (mResultType == RESULT_RIGHT)
         mRightAnimator.start();
        else
         mWrong1Animator.start();
       }
      }
      //正确时 对勾动画
      else if (animation.equals(mRightAnimator)) {
       mRightPercent = (float) animation.getAnimatedValue();
       invalidate();
      }
      //错误时 右侧动画
      else if (animation.equals(mWrong1Animator)) {
       mWrong1Percent = (float) animation.getAnimatedValue();
       invalidate();
       if (mWrong1Percent == 1) {
        mWrong2Animator.start();
       }
      }
      //错误时 左侧动画
      else if (animation.equals(mWrong2Animator)) {
       mWrong2Percent = (float) animation.getAnimatedValue();
       invalidate();
      }
     }
     
     public void setmResultType(int mResultType) {
      this.mResultType = mResultType;
      invalidate();
     }
     
     /**
      * 固定写死了宽高,可重新手动调配
      *
      * @param widthMeasureSpec
      * @param heightMeasureSpec
      */
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
      super.onMeasure(widthMeasureSpec, heightMeasureSpec);
      setMeasuredDimension(dp2px(50), dp2px(50));
     }
    }
    

    5与支付宝效果类似的sercahView(http://www.gcssloop.com/customview/Path_PathMeasure

    s1.PNG s2.PNG

    始状态 初始状态,没有任何动效,只显示一个搜索标志 🔍
    准备搜索 放大镜图标逐渐变化为一个点
    正在搜索 围绕这一个圆环运动,并且线段长度会周期性变化
    准备结束 从一个点逐渐变化成为放大镜图标

    public class SearchView extends View {
    
        // 画笔
        private Paint mPaint;
    
        // View 宽高
        private int mViewWidth;
        private int mViewHeight;
    
        public SearchView(Context context) {
            this(context,null);
        }
    
        public SearchView(Context context, AttributeSet attrs) {
            super(context, attrs);
            initAll();
        }
    
        public void initAll() {
    
            initPaint();
    
            initPath();
    
            initListener();
    
            initHandler();
    
            initAnimator();
    
            // 进入开始动画
            mCurrentState = State.STARTING;
            mStartingAnimator.start();
    
        }
    
        // 这个视图拥有的状态
        public static enum State {
            NONE,
            STARTING,
            SEARCHING,
            ENDING
        }
    
        // 当前的状态(非常重要)
        private State mCurrentState = State.NONE;
    
        // 放大镜与外部圆环
        private Path path_srarch;
        private Path path_circle;
    
        // 测量Path 并截取部分的工具
        private PathMeasure mMeasure;
    
        // 默认的动效周期 2s
        private int defaultDuration = 2000;
    
        // 控制各个过程的动画
        private ValueAnimator mStartingAnimator;
        private ValueAnimator mSearchingAnimator;
        private ValueAnimator mEndingAnimator;
    
        // 动画数值(用于控制动画状态,因为同一时间内只允许有一种状态出现,具体数值处理取决于当前状态)
        private float mAnimatorValue = 0;
    
        // 动效过程监听器
        private ValueAnimator.AnimatorUpdateListener mUpdateListener;
        private Animator.AnimatorListener mAnimatorListener;
    
        // 用于控制动画状态转换
        private Handler mAnimatorHandler;
    
        // 判断是否已经搜索结束
        private boolean isOver = false;
    
        private int count = 0;
    
    
    
        private void initPaint() {
            mPaint = new Paint();
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setColor(Color.WHITE);
            mPaint.setStrokeWidth(15);
            mPaint.setStrokeCap(Paint.Cap.ROUND);
            mPaint.setAntiAlias(true);
        }
    
        private void initPath() {
            path_srarch = new Path();
            path_circle = new Path();
    
            mMeasure = new PathMeasure();
    
            // 注意,不要到360度,否则内部会自动优化,测量不能取到需要的数值
            RectF oval1 = new RectF(-50, -50, 50, 50);          // 放大镜圆环
            path_srarch.addArc(oval1, 45, 359.9f);
    
            RectF oval2 = new RectF(-100, -100, 100, 100);      // 外部圆环
            path_circle.addArc(oval2, 45, -359.9f);
    
            float[] pos = new float[2];
    
            mMeasure.setPath(path_circle, false);               // 放大镜把手的位置
            mMeasure.getPosTan(0, pos, null);
    
            path_srarch.lineTo(pos[0], pos[1]);                 // 放大镜把手
    
            Log.i("TAG", "pos=" + pos[0] + ":" + pos[1]);
        }
    
        private void initListener() {
            mUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    mAnimatorValue = (float) animation.getAnimatedValue();
                    invalidate();
                }
            };
    
            mAnimatorListener = new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animation) {
    
                }
    
                @Override
                public void onAnimationEnd(Animator animation) {
                    // getHandle发消息通知动画状态更新
                    mAnimatorHandler.sendEmptyMessage(0);
                }
    
                @Override
                public void onAnimationCancel(Animator animation) {
    
                }
    
                @Override
                public void onAnimationRepeat(Animator animation) {
    
                }
            };
        }
    
        private void initHandler() {
            mAnimatorHandler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    switch (mCurrentState) {
                        case STARTING:
                            // 从开始动画转换好搜索动画
                            isOver = false;
                            mCurrentState = State.SEARCHING;
                            mStartingAnimator.removeAllListeners();
                            mSearchingAnimator.start();
                            break;
                        case SEARCHING:
                            if (!isOver) {  // 如果搜索未结束 则继续执行搜索动画
                                mSearchingAnimator.start();
                                Log.e("Update", "RESTART");
    
                                count++;
                                if (count>2){       // count大于2则进入结束状态
                                    isOver = true;
                                }
                            } else {        // 如果搜索已经结束 则进入结束动画
                                mCurrentState = State.ENDING;
                                mEndingAnimator.start();
                            }
                            break;
                        case ENDING:
                            // 从结束动画转变为无状态
                            mCurrentState = State.NONE;
                            break;
                    }
                }
            };
        }
    
        private void initAnimator() {
            mStartingAnimator = ValueAnimator.ofFloat(0, 1).setDuration(defaultDuration);
            mSearchingAnimator = ValueAnimator.ofFloat(0, 1).setDuration(defaultDuration);
            mEndingAnimator = ValueAnimator.ofFloat(1, 0).setDuration(defaultDuration);
    
            mStartingAnimator.addUpdateListener(mUpdateListener);
            mSearchingAnimator.addUpdateListener(mUpdateListener);
            mEndingAnimator.addUpdateListener(mUpdateListener);
    
            mStartingAnimator.addListener(mAnimatorListener);
            mSearchingAnimator.addListener(mAnimatorListener);
            mEndingAnimator.addListener(mAnimatorListener);
        }
    
    
        @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);
    
            drawSearch(canvas);
        }
    
        private void drawSearch(Canvas canvas) {
    
            mPaint.setColor(Color.WHITE);
    
    
            canvas.translate(mViewWidth / 2, mViewHeight / 2);
    
            canvas.drawColor(Color.parseColor("#0082D7"));
    
            switch (mCurrentState) {
                case NONE:
                    canvas.drawPath(path_srarch, mPaint);
                    break;
                case STARTING://由放大镜变成点
                    mMeasure.setPath(path_srarch, false);
                    Path dst = new Path();
                    mMeasure.getSegment(mMeasure.getLength() * mAnimatorValue, mMeasure.getLength(), dst, true);
                    canvas.drawPath(dst, mPaint);
                    break;
                case SEARCHING://点绕圆环运动,长度会变(start-stop差值在变)
                    mMeasure.setPath(path_circle, false);
                    Path dst2 = new Path();
                    float stop = mMeasure.getLength() * mAnimatorValue;
                    float start = (float) (stop - ((0.5 - Math.abs(mAnimatorValue - 0.5)) * 200f));
                    mMeasure.getSegment(start, stop, dst2, true);
                    canvas.drawPath(dst2, mPaint);
                    break;
                case ENDING://
                    mMeasure.setPath(path_srarch, false);
                    Path dst3 = new Path();
                    mMeasure.getSegment(mMeasure.getLength() * mAnimatorValue, mMeasure.getLength(), dst3, true);
                    canvas.drawPath(dst3, mPaint);
                    break;
            }
        }
    }
    

    6通过更改起始坐标营造移动的效果,例如充电的波浪

    20160328094655217.gif
     public class GreenWave extends View{
        private Paint mPaint;
        private Path mPath;
        private int mItemWaveLength = 400;
        private int dx;
        public GreenWave(Context context, AttributeSet attrs) {
            super(context, attrs);
            mPath = new Path();
            mPaint = new Paint();
            mPaint.setColor(Color.GREEN);
            mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            mPath.reset();
            int originY = 300;
           int halfWaveLen = mItemWaveLength/2;
            mPath.moveTo(-mItemWaveLength+dx,originY+dx/2);//originY+dx为了波浪垂直位移
            //循环绘制让波纹水平铺满屏幕
            for (int i = -mItemWaveLength;i<=getWidth()+mItemWaveLength;i+=mItemWaveLength){
                mPath.rQuadTo(halfWaveLen/2,-100,halfWaveLen,0);
                mPath.rQuadTo(halfWaveLen/2,100,halfWaveLen,0);
            }
    
            mPath.lineTo(getWidth(),getHeight());
            mPath.lineTo(0,getHeight());
            mPath.close();//闭合之后整体绿色
    
            canvas.drawPath(mPath,mPaint);
        }
    
        public void startAnim(){
            ValueAnimator animator = ValueAnimator.ofInt(0,mItemWaveLength);
            animator.setDuration(2000);
            //animator.setRepeatMode(ValueAnimator.REVERSE);
            animator.setRepeatCount(ValueAnimator.INFINITE);
            animator.setInterpolator(new LinearInterpolator());
            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    dx = (int)animation.getAnimatedValue();
                    postInvalidate();
                }
            });
            animator.start();
        }
    
        }
    
    

    相关文章

      网友评论

          本文标题:动画2-path动画

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