参数
–
目前可配参数:
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
。
三 基础类的实现:
=========
基础类是PathAnimView
和PathAnimHelper
。
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
,
为其设置AnimatorUpdateListener
和onAnimationRepeat
,监听动画的更新和重复。
重点就在这两个监听器里:
public void onAnimationUpdate(ValueAnimator animation) {
//增加一个callback 便于子类重写搞事情
onPathAnimCallback(view, sourcePath, animPath, pathMeasure, animation);
//通知View刷新自己
view.invalidate();
}
动画每次Update的时候,回调onPathAnimCallback()
方法,在里面对animPath做处理。
对AnimPath处理以后,就可以让View绘制新animPath形成动画了:
然后就是让View重绘,这样就会重走onDraw()方法,就是上一节提到的内容。
onPathAnimCallback()
方法也很简单,按动画进度值,取出当前这一小段的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);
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后
这里我特地整理了一份《Android开发核心知识点笔记》,里面就包含了自定义View相关的内容
除了这份笔记,还给大家分享 Android学习PDF+架构视频+面试文档+源码笔记,高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料这几块的内容。非常适合近期有面试和想在技术道路上继续精进的朋友。
分享上面这些资源,希望可以帮助到大家提升进阶,如果你觉得还算有用的话,不妨把它们推荐给你的朋友~
喜欢本文的话,给我点个小赞、评论区留言或者转发支持一下呗~
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-liJ1soWo-1712828875081)]
[外链图片转存中…(img-eRi8Bj1W-1712828875082)]
[外链图片转存中…(img-Pmco6pCJ-1712828875082)]
[外链图片转存中…(img-C2O3qFjo-1712828875082)]
[外链图片转存中…(img-VWYrUMGZ-1712828875082)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后
这里我特地整理了一份《Android开发核心知识点笔记》,里面就包含了自定义View相关的内容
[外链图片转存中…(img-J5I8RyMY-1712828875083)]
除了这份笔记,还给大家分享 Android学习PDF+架构视频+面试文档+源码笔记,高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料这几块的内容。非常适合近期有面试和想在技术道路上继续精进的朋友。
[外链图片转存中…(img-9KrfWL0G-1712828875083)]
分享上面这些资源,希望可以帮助到大家提升进阶,如果你觉得还算有用的话,不妨把它们推荐给你的朋友~
喜欢本文的话,给我点个小赞、评论区留言或者转发支持一下呗~
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!