{"id":13645479,"url":"https://github.com/mcxtzhang/PathAnimView","last_synced_at":"2025-04-21T14:31:11.706Z","repository":{"id":112693037,"uuid":"72724392","full_name":"mcxtzhang/PathAnimView","owner":"mcxtzhang","description":"用于做Path动画的自定义View。 I have a path.I have a view. (Oh~),Path(Anim)View.","archived":false,"fork":false,"pushed_at":"2017-10-26T09:31:11.000Z","size":1177,"stargazers_count":1075,"open_issues_count":6,"forks_count":165,"subscribers_count":34,"default_branch":"master","last_synced_at":"2025-04-04T05:49:18.020Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://blog.csdn.net/zxt0601/article/details/53040506","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mcxtzhang.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2016-11-03T08:32:26.000Z","updated_at":"2025-03-16T11:28:30.000Z","dependencies_parsed_at":"2023-09-12T03:34:54.397Z","dependency_job_id":null,"html_url":"https://github.com/mcxtzhang/PathAnimView","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mcxtzhang%2FPathAnimView","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mcxtzhang%2FPathAnimView/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mcxtzhang%2FPathAnimView/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mcxtzhang%2FPathAnimView/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mcxtzhang","download_url":"https://codeload.github.com/mcxtzhang/PathAnimView/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250070173,"owners_count":21369839,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-02T01:02:35.731Z","updated_at":"2025-04-21T14:31:10.740Z","avatar_url":"https://github.com/mcxtzhang.png","language":"Java","readme":"# PathAnimView\n用于做Path动画的自定义View。\n\n**I have a path.I have a view. (Oh~),Path(Anim)View.**\n\n现已经找到图片-\u003eSVG-\u003ePATH的正确姿势，\n\n**Now i have a pic.I have a view. Oh~,Path(Anim)View.**\n\n相关博文：\n\n实现详解：\n\nhttp://blog.csdn.net/zxt0601/article/details/53040506\n\n图片-\u003eSVG-\u003ePath的正确姿势  ，用法进阶：\n\nhttp://blog.csdn.net/zxt0601/article/details/54018970\n\n\n# 使用：\nStep 1. 在项目根build.gradle文件中增加JitPack仓库依赖。\n```\n    allprojects {\n\t\trepositories {\n\t\t\t...\n\t\t\tmaven { url \"https://jitpack.io\" }\n\t\t}\n\t}\n```\nStep 2. Add the dependency\n```\n\tdependencies {\n\t        compile 'com.github.mcxtzhang:PathAnimView:V1.0.0'\n\t}\n```\n\n\n\n\n# 一 概述\n原本只是想模仿一下我魂牵梦萦的StoreHouse效果，没想到意外撸出来一个工具库。\n\n最简单用法，给我一个path，我还你一个动画。\n\n**I have a path.I have a view. (Oh~),Path(Anim)View.**\n\n```\n    \u003ccom.mcxtzhang.pathanimlib.PathAnimView\n        android:id=\"@+id/pathAnimView1\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"60dp\"\n        android:background=\"@color/blue\"\n        android:padding=\"5dp\"/\u003e\n```\n```\n    Path sPath = new Path();\n    sPath.moveTo(0, 0);\n    sPath.addCircle(40, 40, 30, Path.Direction.CW);\n    pathAnimView1.setSourcePath(sPath);\n```\n\n先看效果图：(真机效果很棒哦，我自己的手机是去年某款599的手机，算是低端的了，6个View一起动画，不会卡，查看GPU呈现模式，95%时间都处于16ms线以下。性能还可以的)\n\n\n![](http://ac-mhke0kuv.clouddn.com/40416b47fdacaebd21be.gif)\n其中\n图1 是普通逐渐填充的效果，无限循环。\n图2 是仿StoreHouse 残影流动效果。（但与原版并不是完全一模一样，估计原版不是用Path做的）\n图3 是逐渐填充的效果，设置了只执行一次。\n图4 是仿StoreHouse效果。数据源来自R.array.xxxx\n图5 是另一种自定义PathAnimHelper实现的自定义动画效果。类似Android L+ 系统进度条效果。\n图6 是仿StoreHouse效果，但是将动画时长设置的很大，所以能看到它逐渐的过程。\n\n---\n#### 2017 01 05 更新：\nI have a pic.I have a view. Oh~,Path(Anim)View.\n效果先随便上几个图，以后**你找到的图有多精彩，gif就有多精彩**：\n\n![随便搜了一个铅笔画的图，丢进去](https://github.com/mcxtzhang/PathAnimView/blob/master/gif/qianbihua.gif)\n\n![支付成功动画](https://github.com/mcxtzhang/PathAnimView/blob/master/gif/success.gif)\n\n![随手复制的二维码icon](https://github.com/mcxtzhang/PathAnimView/blob/master/gif/qrcode.gif)\n\n![来自大佬wing的铁塔](https://github.com/mcxtzhang/PathAnimView/blob/master/gif/tieta.gif)\n\n\n\n---\n\nStoneHouse效果如下简单使用：\n```\n    \u003ccom.mcxtzhang.pathanimlib.StoreHouseAnimView\n        android:id=\"@+id/pathAnimView1\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"60dp\"\n        android:background=\"@android:color/black\"\n        android:padding=\"5dp\"/\u003e\n```\n\n\n```\n        pathAnimView1 = (StoreHouseAnimView) findViewById(R.id.pathAnimView1);\n        Path sPath = new Path();\n        sPath.moveTo(0, 0);\n        sPath.addCircle(40, 40, 30, Path.Direction.CW);\n        pathAnimView1.setSourcePath(PathParserUtils.getPathFromArrayFloatList(StoreHousePath.getPath(\"McXtZhang\")));\n        pathAnimView1.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                pathAnimView1.startAnim();\n            }\n        });\n```\n\n## 参数\n**目前可配**参数：\n1 绘制方面，支持绘制Path的**前景 背景**色。\n```\n    //设置颜色\n    fillView2.setColorBg(Color.WHITE).setColorFg(Color.BLACK);\n```\n\n2 动画方面，目前支持设置动画的**时长**，是否**无限循环**等。\n```\n    //设置了动画总时长，只执行一次的动画\n    fillView2.setAnimTime(3000).setAnimInfinite(false).startAnim();\n```\n\n3 仿StoreHouse风格的View，还支持设置**残影的长度**。\n```\n//设动画时长，设置了stoneHouse残影长度\n    storeView3.setPathMaxLength(1200).setAnimTime(20000).startAnim();\n```\n4 当然你可以拿到Paint自己搞事情：\n```\n    //当然你可以自己拿到Paint，然后搞事情，我这里设置线条宽度\n    pathAnimView1.getPaint().setStrokeWidth(10);\n```\n\n## 数据源：\nPathAnimView的**数据源是Path**。（给我一个Path，还你一个动画View）\n所以内置了几种将别的**资源-\u003ePath**的方法。\n1 直接传string。 StoreHouse风格支持的A-Z,0-9 \".\" \"- \" \" \"（[源自百万大神的库文末也有鸣谢](https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh)，）\n```\n    //根据String 转化成Path\n    setSourcePath(PathParserUtils.getPathFromArrayFloatList(StoreHousePath.getPath(\"ZhangXuTong\", 1.1f, 16)));\n```\n2 定义在R.array.xxx里\n```\n    //动态设置 从StringArray里取\n    storeView2.setSourcePath(PathParserUtils.getPathFromStringArray(this, R.array.storehouse, 3));\n```\n3 简单的SVG(半成品)\n以前从gayHub上找了一个SVG-PATH的转换类：SvgPathParser，现在派上了用场，简单的SVG-PATH，可以，复杂的还有问题，还需要继续寻找更加方案。\n```\n        //SVG转-》path\n        //还在完善中，我从github上找了如下工具类，发现简单的SVG可以转path，复杂点的 就乱了\n/*        SvgPathParser svgPathParser = new SvgPathParser();\n        try {\n            Path path = svgPathParser.parsePath(\"M1,1 L1,50 L50,50 L50,50 L50,1 Z\");\n            storeView3.setSourcePath(path);\n        } catch (ParseException e) {\n            e.printStackTrace();\n        }*/\n```\n\n\n## 简单用法API\n### 1 xml定义\n普通PathAnimView\n效果如图1 3。动画是 进度填充直到满的效果。 \n```\n    \u003ccom.mcxtzhang.pathanimlib.PathAnimView\n        android:id=\"@+id/pathAnimView1\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"60dp\"\n        android:background=\"@color/blue\"\n        android:padding=\"5dp\"/\u003e\n```\n\n高仿StoreHouse风格AnimView：\n这种View显示出来的效果如图2 4 6 。动画是 残影流动的效果。\n```\n    \u003ccom.mcxtzhang.pathanimlib.StoreHouseAnimView\n        android:id=\"@+id/storeView3\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"60dp\"\n        android:background=\"@android:color/black\"\n        android:padding=\"5dp\"/\u003e\n```\n\n### 2 开始动画\n```\n    fillView1.startAnim();\n```\n### 3 停止动画\n```\n    fillView1.stopAnim();\n```\n### 4 清除并停止动画\n```\n    fillView1.clearAnim();\n```\n\n\n## 稍微 ~~搞基~~ 高级点的用法预览\n看到这里细心的朋友可能会发现，上一节，我没有提第5个图View是怎么定义的， 而且第五个View的效果，貌似和其他的不一样，仔细看动画是不是像Android L+的系统自带进度条ProgressBar的效果？\n那说明它的动画效果和我先前提到的两种不一样，是的，一开始我撸是照着StoreHouse那种效果撸的，这是我第二天才扩展的。\n高级的用法，就是本控件动画的**扩展性**。\n你完全可以通过继承`PathAnimHelper类`，重写`onPathAnimCallback（）`方法，扩展动画，图5就是这么来的。\n先讲用法预览，稍后章节会详解。\n**用法**：\n对任意一个普通的PathAnimView，设置一个自定义的PathAnimHelper类即可：\n```\n        //代码示例 动态对path加工，通过Helper\n        pathAnimView1.setPathAnimHelper(new CstSysLoadAnimHelper(pathAnimView1, pathAnimView1.getSourcePath(), pathAnimView1.getAnimPath()));\n```\n\n自定义的PathAnimHelper类:\n```\n/**\n * 介绍：自定义的PathAnimHelper，实现类似Android L+ 进度条效果\n * 作者：zhangxutong\n * 邮箱：zhangxutong@imcoming.com\n * 时间： 2016/11/3.\n */\n\npublic class CstSysLoadAnimHelper extends PathAnimHelper {\n    public CstSysLoadAnimHelper(View view, Path sourcePath, Path animPath) {\n        super(view, sourcePath, animPath);\n    }\n\n    public CstSysLoadAnimHelper(View view, Path sourcePath, Path animPath, long animTime, boolean isInfinite) {\n        super(view, sourcePath, animPath, animTime, isInfinite);\n    }\n\n    @Override\n    public void onPathAnimCallback(View view, Path sourcePath, Path animPath, PathMeasure pathMeasure, ValueAnimator animation) {\n        float value = (float) animation.getAnimatedValue();\n        //获取一个段落\n        float end = pathMeasure.getLength() * value;\n        float begin = (float) (end - ((0.5 - Math.abs(value - 0.5)) * pathMeasure.getLength()));\n        animPath.reset();\n        animPath.lineTo(0, 0);\n        pathMeasure.getSegment(begin, end, animPath, true);\n    }\n}\n```\n\n# 二 逐个介绍\n这里我简单画了一下本文介绍的几个类的类图：\n对于重要方法和属性标注了一下。\n\n\n![](http://ac-mhke0kuv.clouddn.com/1c53f000b03e84b6a9f5.png)\n我们的主角`PathAnimView`继承自View，是一个自定义View。\n它内部持有一个`PathAnimHelper`，专注做**Path动画**。它默认的实现是 **逐渐填充** 的动画效果。\n\n一般情况下只需要更换`PathAnimHelper`,`PathAnimView`即可做出**不同的动画**。（图1第5个View）\n\n但是如果需要扩充一些动画属性**供用户设置**，例如仿StoreHouse风格的动画View，想暴露 **残影长度** 属性供设置。\n我这里采用的是：继承自`PathAnimView`，并增加属性get、set 方法，并重写`getInitAnimHeper()`方法，返回自定义的`PathAnimHelper`。\n如`StoreHouseAnimView`继承自`PathAnimView`,增加了残影长度的get、set方法。并重写`getInitAnimHeper()`方法，返回`StoreHouseAnimHelper`对象。 `StoreHouseAnimHelper`类继承的是`PathAnimHelper`。\n\n先看`PathAnimView`:\n这里我将一些不重要的get、set方法和构造方法剔除，留下比较重要的方法。\n```\n/**\n * 介绍：一个路径动画的View\n * 利用源Path绘制“底”\n * 利用动画Path 绘制 填充动画\n * \u003cp\u003e\n * 一个SourcePath 内含多段Path，循环取出每段Path，并做一个动画,\n * \u003cp\u003e\n * 作者：zhangxutong\n * 邮箱：zhangxutong@imcoming.com\n * 时间： 2016/11/2.\n */\n\npublic class PathAnimView extends View {\n    protected Path mSourcePath;//需要做动画的源Path\n    protected Path mAnimPath;//用于绘制动画的Path\n    protected Paint mPaint;\n    protected int mColorBg = Color.GRAY;//背景色\n    protected int mColorFg = Color.WHITE;//前景色 填充色\n    protected PathAnimHelper mPathAnimHelper;//Path动画工具类\n\n    protected int mPaddingLeft, mPaddingTop;\n\n    public PathAnimView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init();\n    }\n\n    /**\n     * 这个方法可能会经常用到，用于设置源Path\n     *\n     * @param sourcePath\n     * @return\n     */\n    public PathAnimView setSourcePath(Path sourcePath) {\n        mSourcePath = sourcePath;\n        initAnimHelper();\n        return this;\n    }\n\n    /**\n     * INIT FUNC\n     **/\n    protected void init() {\n        //Paint\n        mPaint = new Paint();\n        mPaint.setAntiAlias(true);\n        mPaint.setStyle(Paint.Style.STROKE);\n\n        //动画路径只要初始化即可\n        mAnimPath = new Path();\n\n        //初始化动画帮助类\n        initAnimHelper();\n\n    }\n\n    /**\n     * 初始化动画帮助类\n     */\n    protected void initAnimHelper() {\n        mPathAnimHelper = getInitAnimHeper();\n        //mPathAnimHelper = new PathAnimHelper(this, mSourcePath, mAnimPath, 1500, true);\n    }\n\n    /**\n     * 子类可通过重写这个方法，返回自定义的AnimHelper\n     *\n     * @return\n     */\n    protected PathAnimHelper getInitAnimHeper() {\n        return new PathAnimHelper(this, mSourcePath, mAnimPath);\n    }\n\n    /**\n     * draw FUNC\n     **/\n    @Override\n    protected void onDraw(Canvas canvas) {\n        super.onDraw(canvas);\n        //平移\n        canvas.translate(mPaddingLeft, mPaddingTop);\n\n        mPaint.setColor(mColorBg);\n        canvas.drawPath(mSourcePath, mPaint);\n\n\n        mPaint.setColor(mColorFg);\n        canvas.drawPath(mAnimPath, mPaint);\n    }\n\n    /**\n     * 设置动画 循环\n     */\n    public PathAnimView setAnimInfinite(boolean infinite) {\n        mPathAnimHelper.setInfinite(infinite);\n        return this;\n    }\n\n    /**\n     * 设置动画 总时长\n     */\n    public PathAnimView setAnimTime(long animTime) {\n        mPathAnimHelper.setAnimTime(animTime);\n        return this;\n    }\n\n    /**\n     * 执行循环动画\n     */\n    public void startAnim() {\n        mPathAnimHelper.startAnim();\n    }\n\n    /**\n     * 停止动画\n     */\n    public void stopAnim() {\n        mPathAnimHelper.stopAnim();\n    }\n\n    /**\n     * 清除并停止动画\n     */\n    public void clearAnim() {\n        stopAnim();\n        mAnimPath.reset();\n        mAnimPath.lineTo(0, 0);\n        invalidate();\n    }\n}\n\n```\n代码本身不难，注释也比较详细，核心的话，就是`onDraw()`方法咯：\n我这里用平移做的paddingLeft、paddingTop。\n先利用源Path绘制底边的样子。\n再利用animPath绘制前景，这样animPath不断变化，并且重绘View-\u003eonDraw()，前景就会不断变化，形成动画效果。\n那么核心就是animPath的的变化了，animPath的变化交由 mPathAnimHelper去做。\n```\n    protected void onDraw(Canvas canvas) {\n        super.onDraw(canvas);\n        //平移\n        canvas.translate(mPaddingLeft, mPaddingTop);\n        \n        //先绘制底，\n        mPaint.setColor(mColorBg);\n        canvas.drawPath(mSourcePath, mPaint);\n        \n        //再绘制前景，mAnimPath不断变化，不断重绘View的话，就会有动画效果。\n        mPaint.setColor(mColorFg);\n        canvas.drawPath(mAnimPath, mPaint);\n    }\n```\n\n","funding_links":[],"categories":["其他"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmcxtzhang%2FPathAnimView","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmcxtzhang%2FPathAnimView","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmcxtzhang%2FPathAnimView/lists"}