• 【Android】 给我一个Path,还你一个酷炫动画


    本篇文章已授权微信公众号 hongyangAndroid (鸿洋)独家公布

    转载请标明出处:
    http://blog.csdn.net/zxt0601/article/details/53040506
    本文出自:【张旭童的CSDN】(http://blog.csdn.net/zxt0601)
    代码传送门:喜欢的话。随手点个star。多谢
    https://github.com/mcxtzhang/PathAnimView

    一 概述

    原本仅仅是想模仿一下我魂牵梦萦的StoreHouse效果。没想到意外撸出来一个工具库。

    最简单使用方法,给我一个path(能够有多段),我还你一个动画。

    I have a path.I have a view. (Oh~),Path(Anim)View.

        <com.mcxtzhang.pathanimlib.PathAnimView
            android:id="@+id/pathAnimView1"
            android:layout_width="wrap_content"
            android:layout_height="60dp"
            android:background="@color/blue"
            android:padding="5dp"/>
        Path sPath = new Path();
        sPath.moveTo(0, 0);
        sPath.addCircle(40, 40, 30, Path.Direction.CW);
        pathAnimView1.setSourcePath(sPath);

    先看效果图:(真机效果更棒哦,我自己的手机是去年某款599的手机,算是低端的了,6个View一起动画,不会卡,查看GPU呈现模式,95%时间都处于16ms线下面。性能还能够的)


    当中
    图1 是普通逐渐填充的效果,无限循环。
    图2 是仿StoreHouse 残影流动效果。

    (但与原版并非全然一模一样,预计原版不是用Path做的)
    图3 是逐渐填充的效果,设置了仅仅运行一次。
    图4 是仿StoreHouse效果。数据源来自R.array.xxxx
    图5 是还有一种自己定义PathAnimHelper实现的自己定义动画效果。

    相似Android L+ 系统进度条效果。
    图6 是仿StoreHouse效果,可是将动画时长设置的非常大,所以能看到它逐渐的过程。

    參数

    眼下可配參数:
    1 绘制方面,支持绘制Path的前景 背景色。

        //设置颜色
        fillView2.setColorBg(Color.WHITE).setColorFg(Color.BLACK);

    2 动画方面,眼下支持设置动画的时长,是否无限循环等。

        //设置了动画总时长,仅仅运行一次的动画
        fillView2.setAnimTime(3000).setAnimInfinite(false).startAnim();

    3 仿StoreHouse风格的View,还支持设置残影的长度

    //设动画时长。设置了stoneHouse残影长度
        storeView3.setPathMaxLength(1200).setAnimTime(20000).startAnim();

    4 当然你能够拿到Paint自己搞事情:

        //当然你能够自己拿到Paint,然后搞事情。我这里设置线条宽度
        pathAnimView1.getPaint().setStrokeWidth(10);

    数据源:

    PathAnimView的数据源是Path

    (给我一个Path。还你一个动画View)
    所以内置了几种将别的资源->Path的方法。


    1 直接传string。

    StoreHouse风格支持的A-Z,0-9 “.” “- ” ” “(源自百万大神的库文末也有鸣谢。)

        //依据String 转化成Path
        setSourcePath(PathParserUtils.getPathFromArrayFloatList(StoreHousePath.getPath("ZhangXuTong", 1.1f, 16)));

    2 定义在R.array.xxx里

        //动态设置 从StringArray里取
        storeView2.setSourcePath(PathParserUtils.getPathFromStringArray(this, R.array.storehouse, 3));

    3 简单的SVG(半成品)
    曾经从gayHub上找了一个SVG-PATH的转换类:SvgPathParser,如今派上了用场,简单的SVG-PATH。能够,复杂的还有问题。还须要继续寻找更加方案。
    20170104更新:
    完好的方案已经找到,博文:http://blog.csdn.net/zxt0601/article/details/54018970
    轻松实现图片->SVG->PATH.

            //SVG转-》path
            //还在完好中。我从github上找了例如以下工具类,发现简单的SVG能够转path。复杂点的 就乱了
    /*        SvgPathParser svgPathParser = new SvgPathParser();
            try {
                Path path = svgPathParser.parsePath("M1,1 L1,50 L50,50 L50,50 L50,1 Z");
                storeView3.setSourcePath(path);
            } catch (ParseException e) {
                e.printStackTrace();
            }*/

    简单使用方法API

    1 xml定义

    普通PathAnimView
    效果如图1 3。

    动画是 进度填充直到满的效果。

        <com.mcxtzhang.pathanimlib.PathAnimView
            android:id="@+id/pathAnimView1"
            android:layout_width="wrap_content"
            android:layout_height="60dp"
            android:background="@color/blue"
            android:padding="5dp"/>

    高仿StoreHouse风格AnimView:
    这样的View显示出来的效果如图2 4 6 。动画是 残影流动的效果。

        <com.mcxtzhang.pathanimlib.StoreHouseAnimView
            android:id="@+id/storeView3"
            android:layout_width="wrap_content"
            android:layout_height="60dp"
            android:background="@android:color/black"
            android:padding="5dp"/>

    2 開始动画

        fillView1.startAnim();

    3 停止动画

        fillView1.stopAnim();

    4 清除并停止动画

        fillView1.clearAnim();

    略微 搞基 高级点的使用方法预览

    看到这里细心的朋友可能会发现,上一节。我没有提第5个图View是怎么定义的, 而且第五个View的效果。貌似和其它的不一样,细致看动画是不是像Android L+的系统自带进度条ProgressBar的效果?
    那说明它的动画效果和我先前提到的两种不一样,是的,一開始我撸是照着StoreHouse那种效果撸的,这是我第二天才扩展的。
    高级的使用方法,就是本控件动画的扩展性
    你全然能够通过继承PathAnimHelper类,重写onPathAnimCallback()方法。扩展动画。图5就是这么来的。
    先讲使用方法预览。稍后章节会具体解释。
    使用方法
    对随意一个普通的PathAnimView,设置一个自己定义的PathAnimHelper类就可以:

            //代码演示样例 动态对path加工,通过Helper
            pathAnimView1.setPathAnimHelper(new CstSysLoadAnimHelper(pathAnimView1, pathAnimView1.getSourcePath(), pathAnimView1.getAnimPath()));

    自己定义的PathAnimHelper类:

    /**
     * 介绍:自己定义的PathAnimHelper,实现相似Android L+ 进度条效果
     * 作者:zhangxutong
     * 邮箱:zhangxutong@imcoming.com
     * 时间: 2016/11/3.
     */
    
    public class CstSysLoadAnimHelper extends PathAnimHelper {
        public CstSysLoadAnimHelper(View view, Path sourcePath, Path animPath) {
            super(view, sourcePath, animPath);
        }
    
        public CstSysLoadAnimHelper(View view, Path sourcePath, Path animPath, long animTime, boolean isInfinite) {
            super(view, sourcePath, animPath, animTime, isInfinite);
        }
    
        @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);
        }
    }

    伸手党看到这里假设感兴趣,就能够直接一步gayhub了
    (https://github.com/mcxtzhang/PathAnimView)
    后文比較长。须要自带耐心观看。

    二 架构预览

    这里我简单画了一下本文介绍的几个类的类图:
    对于重要方法和属性标注了一下。


    我们的主角PathAnimView继承自View,是一个自己定义View。
    它内部持有一个PathAnimHelper,专注做Path动画。它默认的实现是 逐渐填充 的动画效果。

    普通情况下仅仅须要更换PathAnimHelper,PathAnimView就可以做出不同的动画。(图1第5个View)

    可是假设须要扩充一些动画属性供用户设置,比如仿StoreHouse风格的动画View。想暴露 残影长度 属性供设置。

    我这里採用的是:继承自PathAnimView。并添加属性get、set 方法,并重写getInitAnimHeper()方法,返回自己定义的PathAnimHelper

    StoreHouseAnimView继承自PathAnimView,添加了残影长度的get、set方法。并重写getInitAnimHeper()方法,返回StoreHouseAnimHelper对象。 StoreHouseAnimHelper类继承的是PathAnimHelper

    三 基础类的实现:

    基础类是PathAnimViewPathAnimHelper

    1 PathAnimView

    先看PathAnimView:
    这里我将一些不重要的get、set方法和构造方法剔除,留下比較重要的方法。

    一个做路径动画的View
    * 利用源Path绘制“底”
    * 利用动画Path 绘制 填充动画
    * 通过mPathAnimHelper 对SourcePath做动画:
    * 一个SourcePath 内含多段Path,循环取出每段Path,并做一个动画


    代码本身不难,凝视也比較具体,核心的话,就是onDraw()方法咯:
    我这里用平移做的paddingLeft、paddingTop。


    先利用源Path(mSourcePath)绘制底边的样子。
    再利用变化的animPath(mAnimPath)绘制前景。这样animPath不断变化,而且重绘View->onDraw(),前景就会不断变化,形成动画效果。
    那么核心就是animPath的的变化了。animPath的变化交由 mPathAnimHelper去做。


    核心源代码例如以下:

    public class PathAnimView extends View {
        protected Path mSourcePath;//须要做动画的源Path
        protected Path mAnimPath;//用于绘制动画的Path
        protected Paint mPaint;
        protected int mColorBg = Color.GRAY;//背景色
        protected int mColorFg = Color.WHITE;//前景色 填充色
        protected PathAnimHelper mPathAnimHelper;//Path动画工具类
    
        protected int mPaddingLeft, mPaddingTop;
    
        public PathAnimView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        /**
         * 这种方法可能会经经常使用到,用于设置源Path
         *
         * @param sourcePath
         * @return
         */
        public PathAnimView setSourcePath(Path sourcePath) {
            mSourcePath = sourcePath;
            initAnimHelper();
            return this;
        }
    
        /**
         * INIT FUNC
         **/
        protected void init() {
            //Paint
            mPaint = new Paint();
            mPaint.setAntiAlias(true);
            mPaint.setStyle(Paint.Style.STROKE);
    
            //动画路径仅仅要初始化就可以
            mAnimPath = new Path();
    
            //初始化动画帮助类
            initAnimHelper();
    
        }
    
        /**
         * 初始化动画帮助类
         */
        protected void initAnimHelper() {
            mPathAnimHelper = getInitAnimHeper();
            //mPathAnimHelper = new PathAnimHelper(this, mSourcePath, mAnimPath, 1500, true);
        }
    
        /**
         * 子类可通过重写这种方法,返回自己定义的AnimHelper
         *
         * @return
         */
        protected PathAnimHelper getInitAnimHeper() {
            return new PathAnimHelper(this, mSourcePath, mAnimPath);
        }
    
        /**
         * draw FUNC
         **/
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            //平移
            canvas.translate(mPaddingLeft, mPaddingTop);
    
            //先绘制底。
            mPaint.setColor(mColorBg);
            canvas.drawPath(mSourcePath, mPaint);
    
            //再绘制前景,mAnimPath不断变化,不断重绘View的话,就会有动画效果。
            mPaint.setColor(mColorFg);
            canvas.drawPath(mAnimPath, mPaint);
        }
    
        /**
         * 设置动画 循环
         */
        public PathAnimView setAnimInfinite(boolean infinite) {
            mPathAnimHelper.setInfinite(infinite);
            return this;
        }
    
        /**
         * 设置动画 总时长
         */
        public PathAnimView setAnimTime(long animTime) {
            mPathAnimHelper.setAnimTime(animTime);
            return this;
        }
    
        /**
         * 运行循环动画
         */
        public void startAnim() {
            mPathAnimHelper.startAnim();
        }
    
        /**
         * 停止动画
         */
        public void stopAnim() {
            mPathAnimHelper.stopAnim();
        }
    
        /**
         * 清除并停止动画
         */
        public void clearAnim() {
            stopAnim();
            mAnimPath.reset();
            mAnimPath.lineTo(0, 0);
            invalidate();
        }
    }
    

    2 PathAnimHelper

    看看最基础的PathAnimHelper类是怎么做的,一样省略一些代码:
    它是一个PathAnimView的Path动画的工具类
    * 一个SourcePath 内含多段(一段)Path,循环取出每段Path。并做一个动画,
    * 默认动画时间1500ms,无限循环
    * 能够通过构造函数改动这两个參数
    * 对外暴露 startAnim() 和 stopAnim()两个方法
    * 子类可通过重写onPathAnimCallback()方法,对animPath进行再次操作,从而定义不同的动画效果

    值得一提的是,这里的动画时间,是指循环取出SourcePath里的N段Path的总时间。


    startAnim()方法是入口,这种方法会在PathAnimView里被调用。
    startAnim()方法里,先初始化一个PathMeasure,以及重置animPath
    然后利用PathMeasure.nextContour()方法,循环一遍SourcePath的Path段数count,
    利用这个count求出每段小Path应该运行的动画时间:totalDuaration / count


    然后便调用loopAnim()方法,循环取出每一段path 。并运行动画。


    loopAnim()方法里,定义一个无限循环的属性动画mAnimator
    为其设置AnimatorUpdateListeneronAnimationRepeat,监听动画的更新和反复。


    重点就在这两个监听器里:

                public void onAnimationUpdate(ValueAnimator animation) {
                    //添加一个callback 便于子类重写搞事情
                    onPathAnimCallback(view, sourcePath, animPath, pathMeasure, animation);
                    //通知View刷新自己
                    view.invalidate();
                }

    动画每次Update的时候,回调onPathAnimCallback()方法。在里面对animPath做处理。
    对AnimPath处理以后,就能够让View绘制新animPath形成动画了:
    然后就是让View重绘。这样就会重走onDraw()方法,就是上一节提到的内容。


    onPathAnimCallback()方法也非常easy,按动画进度值。取出当前这一小段的path的部分路径,赋值给animPath。

        public void onPathAnimCallback(View view, Path sourcePath, Path animPath, PathMeasure pathMeasure, ValueAnimator animation) {
            float value = (float) animation.getAnimatedValue();
            //获取一个段落
            pathMeasure.getSegment(0, pathMeasure.getLength() * value, animPath, true);
        }

    在Repeat监听器里:

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

    由于SourcePath里是可能含有1+段Path的,这里是合适的时机。利用pathMeasure.nextContour();循环取出下一段Path, 推断一下新Path的长度,假设为0,说明这一次大循环结束,即用户视觉上的一次动画进度100%了。


    这里推断我们设置的isInfinite属性,
    假设是true。说明是循环动画。那么做初始化工作:
    清空我们的animPath,初始化pathMeasure。(和startAnim()方法里的初始化工作一致)。
    假设是false,说明动画须要停止,那么手动调用animation.end()停止动画。(图1,第三个动画)


    核心源代码例如以下:

    public class PathAnimHelper {
        protected static final long mDefaultAnimTime = 1500;//默认动画总时间
    
        protected View mView;//运行动画的View
        protected Path mSourcePath;//源Path
        protected Path mAnimPath;//用于绘制动画的Path
        protected long mAnimTime;//动画一共的时间
        protected boolean mIsInfinite;//是否无限循环
    
        protected ValueAnimator mAnimator;//动画对象
    
        public PathAnimHelper(View view, Path sourcePath, Path animPath, long animTime, boolean isInfinite) {
            if (view == null || sourcePath == null || animPath == null) {
                Log.e(TAG, "PathAnimHelper init error: view 、sourcePath、animPath can not be null");
                return;
            }
            mView = view;
            mSourcePath = sourcePath;
            mAnimPath = animPath;
            mAnimTime = animTime;
            mIsInfinite = isInfinite;
        }
    
        /**
         * 运行动画
         */
        public void startAnim() {
            startAnim(mView, mSourcePath, mAnimPath, mAnimTime, mIsInfinite);
        }
    
        /**
         * 一个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();
            //先重置一下须要显示动画的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) { //添加一个callback 便于子类重写搞事情 onPathAnimCallback(view, sourcePath, animPath, pathMeasure, animation); //通知View刷新自己 view.invalidate(); } }); mAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationRepeat(Animator animation) { //每段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(); } /** * 停止动画 */ public void stopAnim() { if (null != mAnimator && mAnimator.isRunning()) { mAnimator.end(); } } /** * 用于子类继承搞事情,对animPath进行再次操作的函数 * * @param view * @param sourcePath * @param animPath * @param pathMeasure */ public void onPathAnimCallback(View view, Path sourcePath, Path animPath, PathMeasure pathMeasure, ValueAnimator animation) { float value = (float) animation.getAnimatedValue(); //获取一个段落 pathMeasure.getSegment(0, pathMeasure.getLength() * value, animPath, true); } }

    至此两个最基础的类就讲完了。如此简单就可实现图1第1、3个动画效果。

    四 实现StoreHouse风格

    我们前面提过,扩展动画。核心是继承PathAnimHelper 重写onPathAnimCallback()方法就可以,所以实现StoreHouse风格。核心类就是StoreHouseAnimHelper

    1 StoreHouseAnimHelper

    • 介绍:仿StoreHouse风格的PathAnimHepler
    • 添加了一个动画残影长度的属性:mPathMaxLength,默认值是400
    • 因没有找到实用的API,这里实现StoreHouse的方法。是手工计算的,不是非常爽。
    • 思路是是循环一遍AnimPath,记录里面每一段小Path的length。
    • 然后再逆序遍历AnimPath,从后面截取 残影长度 的Path,
    • 再复制给AnimPath。

    核心代码例如以下:

    public class StoreHouseAnimHelper extends PathAnimHelper {
        private final static long MAX_LENGTH = 400;
    
        private long mPathMaxLength;//残影路径最大长度
        Path mStonePath;//暂存一下路径,终于要复制给animPath的
        PathMeasure mPm;
        private ArrayList<Float> mPathLengthArray;//路径长度array
        private SparseArray<Boolean> mPathNeedAddArray;//路径是否须要被所有Add的Array
        private int partIndex;//残缺的index
        private float partLength;//残缺部分的长度
    
        public StoreHouseAnimHelper(View view, Path sourcePath, Path animPath, long animTime, boolean isInfinite) {
            super(view, sourcePath, animPath, animTime, isInfinite);
            mPathMaxLength = MAX_LENGTH;
            mStonePath = new Path();
            mPm = new PathMeasure();
            mPathLengthArray = new ArrayList<>();//顺序存放path的length
            mPathNeedAddArray = new SparseArray<>();
        }
    
        @Override
        public void onPathAnimCallback(View view, Path sourcePath, Path animPath, PathMeasure pathMeasure, ValueAnimator animation) {
            super.onPathAnimCallback(view, sourcePath, animPath, pathMeasure, animation);
            //仿StoneHouse效果 ,如今的做法非常挫
            //重置变量
            mStonePath.reset();
            mStonePath.lineTo(0, 0);
            mPathLengthArray.clear();
    
            //循环一遍AnimPath,记录里面每一段小Path的length。

    mPm.setPath(animPath, false); while (mPm.getLength() != 0) { mPathLengthArray.add(mPm.getLength()); mPm.nextContour(); } //逆序遍历AnimPath。记录哪些子Path是须要add的,而且记录那段须要部分add的path的下标 mPathNeedAddArray.clear(); float totalLength = 0; partIndex = 0; partLength = 0; for (int i = mPathLengthArray.size() - 1; i >= 0; i--) { if (totalLength + mPathLengthArray.get(i) <= mPathMaxLength) {//加上了也没满 mPathNeedAddArray.put(i, true); totalLength = totalLength + mPathLengthArray.get(i); } else if (totalLength < mPathMaxLength) {//加上了满了。可是不加就没满 partIndex = i; partLength = mPathMaxLength - totalLength; totalLength = totalLength + mPathLengthArray.get(i); } } //循环Path,并得到终于要显示的AnimPath mPm.setPath(animPath, false); int i = 0; while (mPm.getLength() != 0) { if (mPathNeedAddArray.get(i, false)) { mPm.getSegment(0, mPm.getLength(), mStonePath, true); } else if (i == partIndex) { mPm.getSegment(mPm.getLength() - partLength, mPm.getLength(), mStonePath, true); } mPm.nextContour(); i++; } animPath.set(mStonePath); } }

    2 StoreHouseAnimView

    直接上码了,得益于我们的设计。非常easy:
    重写getInitAnimHeper() 返回我们的StoreHouseAnimHelper,并添加残影长度的get、set方法。

    public class StoreHouseAnimView extends PathAnimView {
    
        public StoreHouseAnimView(Context context) {
            this(context, null);
        }
    
        public StoreHouseAnimView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public StoreHouseAnimView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        /**
         * GET SET FUNC
         **/
        public long getPathMaxLength() {
            return ((StoreHouseAnimHelper) mPathAnimHelper).getPathMaxLength();
        }
    
        /**
         * 设置残影最大长度
         *
         * @param pathMaxLength
         * @return
         */
        public StoreHouseAnimView setPathMaxLength(long pathMaxLength) {
            ((StoreHouseAnimHelper) mPathAnimHelper).setPathMaxLength(pathMaxLength);
            return this;
        }
    
        @Override
        protected PathAnimHelper getInitAnimHeper() {
            return new StoreHouseAnimHelper(this, mSourcePath, mAnimPath);
        }
    }
    

    五 动态扩展动画效果

    前面提过,如图1第五个动画的效果,就是后期我添加扩展的。分析一下这样的效果,它和普通的PathAnimView的效果仅仅有动画不同,也不须要额外引入属性暴露出去供设置,所以这样的场景,我们仅仅须要重写一个PathAnimHelper类,set给PathAnimView就可以。
    代码第一章节也提过。


    一点注意的地方就是,这里没有同第四章节那样调用super.onPathAnimCallback(view, sourcePath, animPath, pathMeasure, animation);
    由于第四章仿StoreHouse的效果,是在第三章的效果基础之上加工而成的。所以须要PathAnimHeper先处理一下。

    而我们这里实现的仿系统ProgressBar的效果。则是全然重写的。


    核心方法例如以下重写,非常easy,不再赘述:

        @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);
        }

    六 总结&待完好:

    总结起来就是 I have a path.I have a view. (Oh~),Path(Anim)View.
    利用这条裤子,仅仅要传一个Path进去,就能够实现多姿多彩的酷炫Path动画,假设对动画不惬意,还能够自己动态扩展

    眼下最急需完好的:
    SVG->Android PATH的转换,
    希望有知道的兄弟能够告知一下,多谢。

    20170104更新:
    完好的方案已经找到,博文:http://blog.csdn.net/zxt0601/article/details/54018970
    轻松实现图片->SVG->PATH.

    代码传送门:喜欢的话,随手点个star。多谢
    https://github.com/mcxtzhang/PathAnimView

    七 引用致谢

    StoreHouse风格点阵资源引用处,也是我第一次看见StoreHouse动画:百万大神的库: https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh
    一開始我看到这样的动画,我还是个小菜鸡。也不知道怎么实现的,可是一直在我脑海里挥之不去。
    后来突然有一天想到能够用Path、PathMeasure做自己定义View动画来实现,就開始动笔写了起来。


    发现Path的路径不太好获取,于是翻看百万大神的库。发现他并非使用的Path动画,可是的的确确是利用点阵设置数据源的。于是我就借助了这些最原始的点阵资源,撸出了这么一个Path动画。

    最初仅仅是想实现这么一个效果。了却我的心愿,没想到还有意外收获。有了这个Path动画工具类库。
    说实话写这么一个东西。我不知不觉也提升了,曾经可能不太会把层级分的这么开,利用继承组合的方式去扩展功能。


    曾经大多还是C V 一份代码改一改,像图上的效果,我可能会分开自己定义三个View去做,复制一些反复代码也不在乎。看来坚持会有收获。
    希望我们都一起进步。

    看大神们都有QQ群,
    向他们靠齐。
    我也建了个QQ搞基交流群:
    557266366 。

    转载请标明出处:
    http://blog.csdn.net/zxt0601/article/details/53040506
    本文出自:【张旭童的CSDN】(http://blog.csdn.net/zxt0601)
    代码传送门:喜欢的话,随手点个star。

    多谢
    https://github.com/mcxtzhang/PathAnimView

  • 相关阅读:
    php使用redis锁
    php接收json数据
    计算机中的二进制、八进制、十进制、十六进制
    mysql8导入myslq5 报错
    如何用最简单的方式解释依赖注入?依赖注入是如何实现解耦的?(通俗易懂)
    【运维】Linux进阶命令简记--Linux(3)
    springboot项目在docker容器中如何优雅关闭
    springboot项目接入sap与部署到docker遇到的问题实录
    如何解决springboot参数传中文乱码
    maven如何动态统一修改版本号
  • 原文地址:https://www.cnblogs.com/llguanli/p/8677179.html
Copyright © 2020-2023  润新知