欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Path绘制动态的贝塞尔曲线、PathMeasure来绘制path动画

程序员文章站 2024-01-14 13:36:46
...

上一篇的波浪曲线是左右重复平移,这次是每一帧绘制一条线,组成上下浮动的曲线,下面是效果图
Path绘制动态的贝塞尔曲线、PathMeasure来绘制path动画

   public WaveView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setStrokeWidth(6);
        paint.setColor(Color.BLUE);
        paint.setStyle(Paint.Style.STROKE);
        path = new Path();
  @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawCircle(200, 200, 90, paint);
        path.reset();
        path.moveTo(waveLength / 2, startY);
        path.rQuadTo(waveLength / 2, -waveHeight, waveLength, 0);
        path.rQuadTo(waveLength / 2, waveHeight, waveLength, 0);
        canvas.drawPath(path, paint);

下面是通过属性动画来改变贝塞尔控制点

 protected void startAnimation() {
        //通过属性动画计算高度
        final ValueAnimator animator = ValueAnimator.ofInt(-waveHeight, waveHeight);
        animator.setDuration(4000);
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                waveHeight = (int) animation.getAnimatedValue();
                postInvalidateDelayed(1);
            }
        });
        animator.setRepeatMode(ValueAnimator.REVERSE);
        animator.start();
    }

path的曲线动画,下面是效果图
Path绘制动态的贝塞尔曲线、PathMeasure来绘制path动画

//全部代码如下:

    private PathMeasure pathM;
    private Bitmap bitmap;
    private int bitHeight;
    private int bitWidth;
    private Rect rect;
    private Matrix matrix;

    public WaveView(Context context) {
        this(context, null);
    }

    public WaveView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setStrokeWidth(6);
        paint.setColor(Color.BLUE);
        paint.setStyle(Paint.Style.STROKE);
        path = new Path();
        //来个path计算器
        pathM = new PathMeasure();
        bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.icon_music);
        bitHeight = bitmap.getHeight();
        bitWidth = bitmap.getWidth();


    }

    private Paint paint;//定义画笔
    private int waveLength = 200;//定义波长度
    private Path path;//曲线路径
    private int waveHeight = 120;//定义波的高度
    private int startY = 500;//起点的Y坐标
    private static String TAG = "test";
    private float fraction = 0.0f;
    private float[] position = new float[2];
    private float[] tange = new float[2];


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawCircle(200, 200, 90, paint);
        path.reset();
        path.moveTo(waveLength / 2, startY);
        path.rQuadTo(waveLength / 2, -waveHeight, waveLength, 0);
        path.rQuadTo(waveLength / 2, waveHeight, waveLength, 0);
        canvas.drawPath(path, paint);
        pathM.setPath(path, false);
        float length = pathM.getLength();
        Log.i(TAG, "WaveView: 长度==" + length);
        boolean flag = pathM.getPosTan(fraction * length, position, tange);
        if (flag) {
            //开始绘制运行轨迹
            canvas.drawBitmap(bitmap, position[0] - bitWidth, position[1] - bitHeight, paint);
        }
    }


    protected void computePercent() {
        ValueAnimator animator = ValueAnimator.ofFloat(0, 1.0f);
        animator.setDuration(4000);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                fraction = (float) animation.getAnimatedValue();
                Log.i(TAG, "onAnimationUpdate: 百分比==" + fraction);
                postInvalidateDelayed(5);
            }
        });
        animator.start();
    }

上面有点缺陷就是“音乐符号”的运动角度没有设置和调整,调整后的效果如右边所示:Path绘制动态的贝塞尔曲线、PathMeasure来绘制path动画

 public WaveView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setStrokeWidth(6);
        paint.setColor(Color.BLUE);
        paint.setStyle(Paint.Style.STROKE);
        path = new Path();
        //新增代码。来个path计算器
        pathM = new PathMeasure();
        bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.icon_music);
        bitHeight = bitmap.getHeight();
        bitWidth = bitmap.getWidth();
        matrix=new Matrix();


    }

    private Paint paint;//定义画笔
    private int waveLength = 200;//定义波长度
    private Path path;//曲线路径
    private int waveHeight = 120;//定义波的高度
    private int startY = 500;//起点的Y坐标
    private static String TAG = "test";
    private float fraction = 0.0f;
    private float[] position = new float[2];
    private float[] tange = new float[2];


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawCircle(200, 200, 90, paint);
        path.reset();
        path.moveTo(waveLength / 2, startY);
        path.rQuadTo(waveLength / 2, -waveHeight, waveLength, 0);
        path.rQuadTo(waveLength / 2, waveHeight, waveLength, 0);
        canvas.drawPath(path, paint);
        pathM.setPath(path, false);
        float length = pathM.getLength();
        Log.i(TAG, "WaveView: 长度==" + length);
        boolean flag = pathM.getPosTan(fraction * length, position, tange);
        if (flag) {
            //开始绘制运行轨迹
//            canvas.drawBitmap(bitmap, position[0] - bitWidth, position[1] - bitHeight, paint);
            matrix.reset();
            float degree = (float) (Math.atan2(tange[1], tange[0])*180f/Math.PI);
            matrix.postRotate(degree,bitmap.getWidth()/2,bitmap.getHeight()/2);
            matrix.postTranslate(position[0]-bitmap.getWidth()/2,position[1]-bitmap.getHeight()/2);
            canvas.drawBitmap(bitmap,matrix,paint);

        }
    }


    protected void computePercent() {
        ValueAnimator animator = ValueAnimator.ofFloat(0, 1.0f);
        animator.setDuration(4000);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                fraction = (float) animation.getAnimatedValue();
                Log.i(TAG, "onAnimationUpdate: 百分比==" + fraction);
                postInvalidateDelayed(5);
            }
        });
        animator.start();
    }

总结:还有一种比较好的测量 方式,直接返回matrix,这个matrix里面包含了delt x和y,以及tang角度。


        boolean flag = pathM.getMatrix(fraction * length, matrix, PathMeasure.POSITION_MATRIX_FLAG | PathMeasure.TANGENT_MATRIX_FLAG);
        if (flag) {
            canvas.drawBitmap(bitmap, matrix, paint);
        }

新测量方式的源码截取和解释如下

  /**
     * Pins distance to 0 <= distance <= getLength(), and then computes the
     * corresponding matrix. Returns false if there is no path, or a zero-length
     * path was specified, in which case matrix is unchanged.
     *
     * @param distance The distance along the associated path
     * @param matrix Allocated by the caller, this is set to the transformation
     *        associated with the position and tangent at the specified distance
     * @param flags Specified what aspects should be returned in the matrix.
     */
    public boolean getMatrix(float distance, Matrix matrix, int flags) {
        return native_getMatrix(native_instance, distance, matrix.native_instance, flags);
    }