path 动画

Paint

Paint类包含关于如何绘制几何图形,文本和位图的样式和颜色信息。

//设置抗锯齿,如果不设置,加载位图的时候可能会出现锯齿状的边界,如果设置,边界就会变的稍微有点模糊,锯齿就看不到了
mPaint.setAntiAlias(boolean aa);
设置绘画的风格,用于控制原始图形的几何解释(除了drawBitmap,它总是假定为Fill)。
//设置绘制的风格,用于控制原语的几何体是如何解释的(除了drawBitmap,它总是假定为Fill)。
mPaint.setStyle(Style style);
//设置描边的宽度。 在发线模式下将0传递给笔划。 细线总是绘制一个独立于Canva矩阵的像素。设置绘画的笔画宽度,每当绘画时使用风格是Stroke或StrokeAndFill
mPaint.setStrokeWidth(3);

//用这种风格绘制的几何和文本将被填充,忽略所有
中风相关的设置在油漆。
Paint.Style.FILL
//用这种风格绘制的几何和文本将描边,尊重油漆上的笔画相关的领域。
Paint.Style.STROKE
//用这种风格绘制的几何和文字将同时填充和描边,尊重油漆上的笔触相关的领域。 如果几何形状逆时针方向,此模式可能会产生意想不到的结果。 此限制不适用于FILL或STROKE。
Paint.Style.FILL_AND_STROKE

mPaint.setColor(mColorBg);

path

Path类封装了由直线段,二次曲线和三次曲线组成的复合(多个轮廓)几何路径。可以使用canvas.drawPath(path,paint)绘制,可以是填充或描边(基于绘制的样式) ,或者它可以用于剪辑或在路径上绘制文本。

//清除路径中的任何线条和曲线,使其为空。这不会改变填充类型的设置。
animPath.reset();
//从最后一个点到指定点(x,y)添加一条线。 如果此轮廓未进行moveTo()调用,则第一个点将自动设置为(0,0)。
x一行结尾的x坐标--y一行结尾的y坐标
animPath.lineTo(0, 0);

将下一个轮廓的起点设置为点(x,y)。
@参数x新轮廓起点的x坐标
@参数y新轮廓起点的y坐标
sPath.moveTo
向路径添加一个封闭的圆形轮廓
参数x要添加到路径的圆的中心的x坐标
参数y要添加到路径中的圆的中心的y坐标
radius要添加到路径的圆的半径
dir旋转圆的轮廓的方向
sPath.addCircle(50, 50, 60, Path.Direction.CW);
顺时针
Path.Direction.CW
逆时针
Path.Direction.CCW
创建一个空的PathMeasure对象。 要使用它来测量路径的长度,和/或沿着它找到位置和切线,请调用setPath。
请注意,一旦一个路径与度量对象相关联,如果路径随后被修改并且使用度量对象,那么它是未定义的。 如果修改路径,则必须使用路径调用setPath。
PathMeasure pathMeasure = new PathMeasure();
指定一个新的路径,或者为null,否则为空。
pathMeasure.setPath(sourcePath, false);
//返回当前轮廓的总长度,如果没有路径与此度量对象关联,则返回0。
pathMeasure.getLength()
移动到路径中的下一个轮廓。 如果存在则返回true,否则返回false。
pathMeasure.nextContour();

//经过上面这段计算duration代码的折腾 需要重新初始pathMeasure
pathMeasure.setPath(sourcePath, false);
  //每段path走完后,要补一下 某些情况会出现 animPath不满的情况(当然也可以设置一段,就可以达到进度条的效果)
*给定起止距离,返回到中间段。 如果段是零长度,则返回false,否则返回true。 startD和stopD被固定为合法值(0..getLength())。 如果startD> = stopD,则返回false(并保持dst不变)。如果startWithMoveTo为true,则使用moveTo开始该段。
      * 

在{@link android.os.Build.VERSION_CODES#KITKAT}和更早的发行版上,得到的路径可能不会显示在硬件加速的画布上。 一个简单的解决方法是向这个路径添加一个单独的操作,比如 dst.rLineTo(0,0) pathMeasure.getSegment(0, pathMeasure.getLength(), animPath, true); 给定一个起点和终点距离,返回中间段。 如果段是零长度,则返回false,否则返回true。 startD和stopD被固定为合法值(0..getLength())。 如果startD> = stopD,则返回false(并保持dst不变)。 如果startWithMoveTo为true,则使用moveTo开始片段。 在{@link android.os.Build.VERSION_CODES#KITKAT}和更早版本上,结果路径可能不会显示在硬件加速画布上。 一个简单的解决方法是向这个路径添加一个单独的操作,比如 dst.rLineTo(0,0) //animPath替换成mStonePath animPath.set(mStonePath);

view

当这个视图的大小改变时,这在layout期间被调用。 如果刚刚添加到视图层次结构中,则使用旧值0调用。
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
       mPaddingLeft = getPaddingLeft();
        mPaddingTop = getPaddingTop();
    }

返回这个视图的左边填充。 如果有插入和启用滚动条,则此值可能还包括显示滚动条所需的空间。
        mPaddingLeft = getPaddingLeft();
        mPaddingTop = getPaddingTop();

invalidate

invalidate,请求重新draw,只会绘制调用者本身。
      * 

这必须从UI线程调用。 要从非UI线程调用,请调用{@link #postInvalidate()}。 public void invalidate() { invalidate(true); } 这是invalidate()工作实际发生的地方。 一个完整的invalidate()将导致绘图缓存失效,但是可以使用invalidateCache设置为false来调用此函数,以跳过不需要它的情况下的失效步骤(例如,保持与 相同的内容)。 * * @param invalidateCache此视图的绘图缓存是否也应该失效。 对于完全无效,这通常是正确的,但是如果视图的内容或维度没有改变,则可以将其设置为假。 void invalidate(boolean invalidateCache) { invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true); }

Canvas

Canvas类保存“draw”调用。 要绘制东西,你需要4个基本组件:一个位图来保存像素,一个Canvas来承载绘制调用(写入到位图),一个绘制基元(例如Rect,Path,文本,位图)和一个绘制 描述绘图的颜色和样式)。

//用指定的转换对当前矩阵进行预处理
canvas.translate(mPaddingLeft, mPaddingTop);
*使用指定的绘图绘制指定的路径。 该路径将根据画笔中的样式进行填充或框定。
     *
      * @param path要绘制的路径
      * @param paint用于绘制路径的油漆
canvas.drawPath(mSourcePath, mPaint);
canvas.drawPath(mAnimPath, mPaint);
绘制前景,mAnimPath不断变化,不断重绘View的话,就会有动画效果。

PathAnimView

一个路径动画的View,利用源Path绘制“底”,利用动画Path 绘制 填充动画
一个SourcePath 内含多段Path,循环取出每段Path,并做一个动画
需要做动画的源Path
用于绘制动画的Path
背景色
前景色

PathAnimHelper

介绍:一个自定义View Path动画的工具类
一个SourcePath 内含多段(一段)Path,循环取出每段Path,并做一个动画,

  /**
     * 一个SourcePath 内含多段Path,循环取出每段Path,并做一个动画
     * 自定义动画的总时间
     * 和是否循环
     *
     * @param view           需要做动画的自定义View
     * @param sourcePath     源Path
     * @param animPath       自定义View用这个Path做动画
     * @param totalDuaration 动画一共的时间
     * @param isInfinite     是否无限循环
     */
    protected void startAnim(View view, Path sourcePath, Path animPath, long totalDuaration, boolean isInfinite) {
        if (view == null || sourcePath == null || animPath == null) {
            return;
        }
        PathMeasure pathMeasure = new PathMeasure();
        //pathMeasure.setPath(sourcePath, false);
        //先重置一下需要显示动画的path
        animPath.reset();
        animPath.lineTo(0, 0);
        pathMeasure.setPath(sourcePath, false);
        //这里仅仅是为了 计算一下每一段的duration
        int count = 0;
        while (pathMeasure.getLength() != 0) {
            pathMeasure.nextContour();
            count++;
        }
        //经过上面这段计算duration代码的折腾 需要重新初始化pathMeasure
        pathMeasure.setPath(sourcePath, false);
        loopAnim(view, sourcePath, animPath, totalDuaration, pathMeasure, totalDuaration / count, isInfinite);
    }

    /**
     * 循环取出每一段path ,并执行动画
     *
     * @param animPath    自定义View用这个Path做动画
     * @param pathMeasure 用于测量的PathMeasure
     */
    protected void loopAnim(final View view, final Path sourcePath, final Path animPath, final long totalDuaration, final PathMeasure pathMeasure, final long duration, final boolean isInfinite) {
        //动画正在运行的话,先stop吧。万一有人要使用新动画呢,(正经用户不会这么用。)
        stopAnim();

        mAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
        mAnimator.setInterpolator(new LinearInterpolator());
        mAnimator.setDuration(duration);
        mAnimator.setRepeatCount(ValueAnimator.INFINITE);
        mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                //Log.i("TAG", "onAnimationUpdate");
                //增加一个callback 便于子类重写搞事情
                onPathAnimCallback(view, sourcePath, animPath, pathMeasure, animation);
                //通知View刷新自己
                view.invalidate();
            }
        });

        mAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationRepeat(Animator animation) {
                //Log.w("TAG", "onAnimationRepeat: ");
                //每段path走完后,要补一下 某些情况会出现 animPath不满的情况
                pathMeasure.getSegment(0, pathMeasure.getLength(), animPath, true);

                //绘制完一条Path之后,再绘制下一条
                pathMeasure.nextContour();
                //长度为0 说明一次循环结束
                if (pathMeasure.getLength() == 0) {
                    if (isInfinite) {//如果需要循环动画
                        animPath.reset();
                        animPath.lineTo(0, 0);
                        pathMeasure.setPath(sourcePath, false);
                    } else {//不需要就停止(因为repeat是无限 需要手动停止)
                        animation.end();
                    }
                }
            }

        });
        mAnimator.start();
    }

根据ArrayList path 解析

    /**
     * 根据ArrayList path 解析
     *
     * @param path
     * @return
     */
    public static Path getPathFromArrayFloatList(ArrayList path) {
        Path sPath = new Path();
        for (int i = 0; i < path.size(); i++) {
            float[] floats = path.get(i);
            sPath.moveTo(floats[0], floats[1]);
            sPath.lineTo(floats[2], floats[3]);
        }
        return sPath;
    }
    /**
     * 从R.array.xxx里取出点阵,
     *
     * @param context
     * @param arrayId
     * @param zoomSize
     * @return
     */
    public static Path getPathFromStringArray(Context context, int arrayId, float zoomSize) {
        Path path = new Path();
        String[] points = context.getResources().getStringArray(arrayId);
        for (int i = 0; i < points.length; i++) {
            String[] x = points[i].split(",");
            for (int j = 0; j < x.length; j = j + 2) {
                if (j == 0) {
                    path.moveTo(Float.parseFloat(x[j]) * zoomSize, Float.parseFloat(x[j + 1]) * zoomSize);
                } else {
                    path.lineTo(Float.parseFloat(x[j]) * zoomSize, Float.parseFloat(x[j + 1]) * zoomSize);
                }
            }
        }
        return path;
    }
        float[][] LETTERS = new float[][]{
                new float[]{
                        // A
                        24, 0, 1, 22,
                        1, 22, 1, 72,
                        24, 0, 47, 22,
                        47, 22, 47, 72,
                        1, 48, 47, 48
                },

                new float[]{
                        // B
                        0, 0, 0, 72,
                        0, 0, 37, 0,
                        37, 0, 47, 11,
                        47, 11, 47, 26,
                        47, 26, 38, 36,
                        38, 36, 0, 36,
                        38, 36, 47, 46,
                        47, 46, 47, 61,
                        47, 61, 38, 71,
                        37, 72, 0, 72,
                },....
};

进度条效果

    @Override
    public void onPathAnimCallback(View view, Path sourcePath, Path animPath, PathMeasure pathMeasure, ValueAnimator animation) {
        float value = (float) animation.getAnimatedValue();
        //获取一个段落
        float end = pathMeasure.getLength() * value;
        float begin = (float) (end - ((0.5 - Math.abs(value - 0.5)) * pathMeasure.getLength()));
        animPath.reset();
        animPath.lineTo(0, 0);
        pathMeasure.getSegment(begin, end, animPath, true);
    }

你可能感兴趣的:(path 动画)