前言
创建这个库并非是由于某个需求,而是以前在阅读OkHttp源码时深感设计的精妙,一直有一个模仿其责任链模式做一个自定义View(SimpleLineView)的想法,一是为了好玩,二是希望能够抛砖引玉。
对于View的path动画,PathAnimView甚至是Lottie等都可以作出十分复杂酷炫的path动画。如果你的动画很复杂很酷炫,这个库可能就不太适合了。
当然,SimpleLineView也有自己的优势:
1、可随意定制路径
2、路径可以随意组合
3、支持progress
效果图
整体架构
1、自定义Painter(绘制相关接口),提供绘制功能。
2、RealChain实现了Chain接口并且维护了一个Painter的list,控制所有Painter依次执行绘制。
3、SimpleLineView维护了一个RealChain并且对外提供了方法,用于添加Painter以及控制动画的启动、停止和继续。在onDraw里调用当前Painter的onDraw方法实现真正的绘制。
使用
1 | // 圆形 |
前期准备:PixelPath
1 | // 横向像素 |
这里的mHorizontal(横向格子数)和mVertical(纵向格子数)都为4。如果mPath为{1, 13, 16, 4}, 则绘制的图形为依次连接1,13,16,4的矩形(是否封闭可设置对应参数)。
如果图形的形状比较复杂,可以用PS打开图片,依次获取像素点的x和y值(这里x和y值的单位可以是像素、厘米等,但是计算时要与图像大小的单位一致)。假设图像宽为w, 高为h, 则当前点的值为 w * ( y - 1) + x。
Painter接口
Painter接口主要提供了绘制的功能以及绘制时所需要的一些参数。
1 | public interface Painter { |
onDraw方法和View的onDraw方法一样实现绘制。这里主要介绍一下completeDraw方法。由于每个Painter是依次绘制的,当下一个Painter进行绘制时,当前Pianter的形状也需要绘制,所以这里的completeDraw应该是当前Painter所要绘制的完整形状。这个方法会被当前Painter之后的每一个Painter调用。有点绕,看一下抽象类AbstractPainter的onDraw实现:
1 |
|
drawPreviouse方法:
1 | /** |
为了方便说明,看一下AbstractPainter的一个子类RealCirclePainter的completeDraw和realDraw方法:
1 |
|
mSweepAngle是所需扫过的角度,angle()为当前的角度大小,这个角度会随着时间递增,如此也就有了动画。
当然必须得看一下AbstractPainter的start方法,这个才是每个Painter开始的地方。
1 |
|
fetchCoordinate方法为绘制提供了坐标点,实现下文会介绍。之后通过performDraw方法来开始View的绘制,看一下AbstractPainter另一个子类SegmentPainter的performDraw方法实现:
1 |
|
在performDraw方法中会遍历PixelPoint的list,每隔INTERVAL时间调用Action接口的update方法更新View一次。
Chain接口
Chain主要提供了调控的功能。
1 | public interface Chain { |
看一下唯一实现类RealChain的proceed方法
1 |
|
这里照搬了OkHttp,通过在RealChain的procced方法创建新的RealChain对象实现Painter的依次执行。由于Painter都是自定义的,所以当index等于所有Painter的size时return就好了,而OkHttp的最后一个Interceptor是没有创建Chain的。
Action接口
Action接口主要提供了计算当前path实际坐标点、通知更新View以及设置或者获取View的所需的参数的功能,这也是View需要实现的接口。
1 | public interface Action { |
看一下fetchCoordinate方法:
1 |
|
Utils的setPoint方法:
1 | public static void setPoint(Painter painter, List<PixelPoint> pixelPoints, int width, int height) { |
用的小学数学,看一下注释就好了。接着看一下update和onDraw方法:
1 |
|
在update中设置当前的painter,由于之前的操作在线程中,这里调用postInvalidate通知绘制,在View的onDraw方法中调用Painter的onDraw实现绘制。
其他
介绍完三个接口,整体的流程算是介绍完了,下面看一下两个功能型的Painter。
DelayPainter
1 |
|
在start方法通过Thread.sleep进行延时,如果当前方法在主线程执行就抛出异常。
TaskPainter
1 |
|
当Painter有多个时,计算会耗费一定的时间,这里将chain的procced置于线程中,让后续的过程都在线程中执行以保证动画的流畅。并且,当start动画时,将PixelPoint的状态重置,保证下一次绘制是一个完整的过程。
不足
1、图形较为复杂时,通过PS获取计算坐标点较为繁琐。
2、由于计算在子线程,绘制在主线程,当SimpleLineView设置progress过快时,上一步onDraw可能未完成,画面可能会闪动,暂时的解决方法是将此过程放入主线程。使用代码如下:
1 | mView.addPainter(mCicleProgressPainter).addPainter(mHookProgressPainter).onMain(); |
OK,基本上介绍完了,如果有什么不足或者有什么问题欢迎指正!