Materialish Progress 源码阅读.docx
- 文档编号:6795570
- 上传时间:2023-01-10
- 格式:DOCX
- 页数:11
- 大小:53.03KB
Materialish Progress 源码阅读.docx
《Materialish Progress 源码阅读.docx》由会员分享,可在线阅读,更多相关《Materialish Progress 源码阅读.docx(11页珍藏版)》请在冰豆网上搜索。
MaterialishProgress源码阅读
Material-ishProgress源码阅读
很明显第一个控件是默认情况下的,而第二个就是我改动过的控件。
这个控件的制作,我们先着重来看动效部分,毕竟如果静止状态的话其实是有很多种方法来实现的,第一步我们先不去理睬静态这一部分。
撇去细节,我们从大的方向入手,我把这个动效分为两个部分:
第一部分:
圆弧的动态绘制。
这一部分,只要看第二个progress就行了,这里要做的就是计算每一段时间内要绘制的部分,不管是最初的由最小变最大,还是之后的最大变最小。
第二部分:
圆弧的滚动。
其实准确来说,是对起始角度的计算。
第一个progress融合了第一部分和第二部分,圆弧的大小和起始点的不断变化的结合就是一个完整的materialprogressbar。
分析前的准备
API:
//从开机到现在的毫秒数
SystemClock.uptimeMillis()
//绘制圆弧
canvas.drawArc(circleBounds,from,length,false,barPaint);
函数:
//在开始与结束的地方速率改变比较慢,在中间的时候加速
(float)(Math.cos((input+1)*Math.PI)/2.0f)+0.5f
//在开始的地方速率改变比较慢,然后开始加速
Math.pow(input,2)
//在开始的地方快然后慢
1.0f-(1.0f-input)*(1.0f-input)
以上只是部分函数,如果想要看更多,可以看看我的这篇简单的插值器文章Android动画-Interpolator(插值器)大全,里面介绍了常用的插值器,想要知道怎么实现的,看看源码就知道了,其实就是一些函数的使用,数学都还给我体育老师了,这个没法讲,希望有好文章的童鞋可以推荐下,不胜感激。
列出这些函数的意义其实就是想告诉看源码的童鞋,看源码不要纠结在细节上,有些东西确实不是一时半会儿能弄懂的。
对于本章的含义就是,不要纠结于函数的由来,只需要知道这是用来做什么的。
还有一些参数我就不一一介绍了,都是比较简单的参数,于逻辑无碍。
到此,因该说基本的准备都OK了,接下来就是看源码了。
思路
从不熟悉控件源码的角度来讲,这里就讲思路确实不太合适,但是在接下来的源码解析中,却很难穿插思路的讲解,因为每一行代码都多少需要联系到其它的代码才能准确解读出代码的意义,而且还有很多细节的处理,那时候再来说思路就显得过于混乱了,如果能在这里明白控件的思路,我觉得看者完全可以自己去看源码,相信会比我这生涩的分析更加清晰。
这里的思路也是需要联系到前面介绍的参数,这样讲解会更清晰点,我们从控件伊始开始分析:
控件从最小圆弧角度barLength开始,经历了pausedTimeWithoutGrowing(pausedTimeWithoutGrowing>=pauseGrowingTime)的时间后,开始计算需要绘制的总角度:
总角度=barLength+需要额外绘制的角度。
而这个需要额外绘制的角度=已经经历的时间×(barMaxLength-barLength)/barSpinCycleTime。
而当到达最大角度之后,我们就需要减去相应的时间内对应的角度:
需要额外绘制的角度=(barMaxLength-barLength)-已经经历的时间×(barMaxLength-barLength)/barSpinCycleTime,前者的计算是为了圆弧逐渐增大,后者则是为了逐渐变小。
最后,需要小小的强调下:
起始角度的计算也非常重要。
源码分析
控件的全部代码只有800行,有效代码目测只有1/3,这里我们就不看onMeasure和onSizeChanged这些准备工作的处理代码了,只是一些测量和初始化的工作代码,那么onDraw里面的代码以及涉及到的相关函数每一行都是必不可少的。
onDraw
protectedvoidonDraw(Canvascanvas){
super.onDraw(canvas);
//圆弧背景
canvas.drawArc(circleBounds,360,360,false,rimPaint);
booleanmustInvalidate=false;
if(!
shouldAnimate){
return;
}
if(isSpinning){
//Drawthespinningbar
mustInvalidate=true;
longdeltaTime=(SystemClock.uptimeMillis()-lastTimeAnimated);
updateBarLength(deltaTime);
floatdeltaNormalized=deltaTime*spinSpeed/1000.0f;
//mProgress+=deltaNormalized;
if(mProgress>360){
mProgress-=360f;
//Afullturnhasbeencompleted
//werunthecallbackwith-1incasewewantto
//dosomething,likechangingthecolor
runCallback(-1.0f);
}
lastTimeAnimated=SystemClock.uptimeMillis();
floatfrom=mProgress-90;
floatlength=barLength+barExtraLength;
if(isInEditMode()){
from=0;
length=135;
}
canvas.drawArc(circleBounds,from,length,false,barPaint);
}else{
floatoldProgress=mProgress;
if(mProgress!
=mTargetProgress){
//Wesmoothlyincreasetheprogressbar
mustInvalidate=true;
floatdeltaTime=(float)(SystemClock.uptimeMillis()-lastTimeAnimated)/1000;
floatdeltaNormalized=deltaTime*spinSpeed;
mProgress=Math.min(mProgress+deltaNormalized,mTargetProgress);
lastTimeAnimated=SystemClock.uptimeMillis();
}
if(oldProgress!
=mProgress){
runCallback();
}
floatoffset=0.0f;
floatprogress=mProgress;
if(!
linearProgress){
Log.d(TAG,"dofactor");
floatfactor=2.0f;
offset=(float)(1.0f-Math.pow(1.0f-mProgress/360.0f,2.0f*factor))*360.0f;
progress=(float)(1.0f-Math.pow(1.0f-mProgress/360.0f,factor))*360.0f;
}
if(isInEditMode()){
progress=360;
}
canvas.drawArc(circleBounds,offset-90,progress,false,barPaint);
}
if(mustInvalidate){
invalidate();
}
}
在参数准备的环节已经介绍了isSpinning这个参数,所以当isSpinning为true的时候,便是我们首要分析的代码部分–动效代码。
if(isSpinning==true)
mustInvalidate=true;
longdeltaTime=(SystemClock.uptimeMillis()-lastTimeAnimated);
updateBarLength(deltaTime);
floatdeltaNormalized=deltaTime*spinSpeed/1000.0f;
mProgress+=deltaNormalized;
if(mProgress>360){
mProgress-=360f;
//Afullturnhasbeencompleted
//werunthecallbackwith-1incasewewantto
//dosomething,likechangingthecolor
runCallback(-1.0f);
}
lastTimeAnimated=SystemClock.uptimeMillis();
floatfrom=mProgress-90;
floatlength=barLength+barExtraLength;
if(isInEditMode()){
from=0;
length=135;
}
canvas.drawArc(circleBounds,from,length,false,barPaint);
在onDraw方法调用时,每次都初始化一个参数booleanmustInvalidate=false;,这个参数便是判断是否重绘当前控件,在isSpinning为true的情况下,很明显这是一个死循环,在onDraw的最后几行:
if(mustInvalidate){
invalidate();
}
这里的判断保证了无线循环。
longdeltaTime=(SystemClock.uptimeMillis()-lastTimeAnimated);
相对于之前绘制的时间,到现在的需要绘制的时间的时间差。
如果你看了思路部分的文字的话,那么很方便就可以将这个变量理解为已经历的时间。
这个参数不但是用来计算圆弧需要绘制角度的重要参数,还是用来计算圆弧起始角度的重要参数。
lastTimeAnimated的赋值
在控件显示的时候
@Override
protectedvoidonVisibilityChanged(ViewchangedView,intvisibility){
super.onVisibilityChanged(changedView,visibility);
if(visibility==VISIBLE){
lastTimeAnimated=SystemClock.uptimeMillis();
}
}
还有就是在完成了一系列的运算之后,最后给lastTimeAnimated赋值
lastTimeAnimated=SystemClock.uptimeMillis();
updateBarLength(deltaTime)用于计算圆弧额外绘制角度的函数,非常重要!
可以说看懂这个函数就看懂了一切。
需要注意的是,这个函数的分析需要和onDraw方法结合看才行。
privatevoidupdateBarLength(longdeltaTimeInMilliSeconds){
if(pausedTimeWithoutGrowing>=pauseGrowingTime){
timeStartGrowing+=deltaTimeInMilliSeconds;
if(timeStartGrowing>barSpinCycleTime){
//Wecompletedasizechangecycle
//(growingorshrinking)
timeStartGrowing-=barSpinCycleTime;
//if(barGrowingFromFront){
pausedTimeWithoutGrowing=0;
//}
barGrowingFromFront=!
barGrowingFromFront;
}
floatdistance=
(float)Math.cos((timeStartGrowing/barSpinCycleTime+1)*Math.PI)/2+0.5f;
floatdestLength=(barMaxLength-barLength);
if(barGrowingFromFront){
barExtraLength=distance*destLength;
}else{
floatnewLength=destLength*(1-distance);
mProgress+=(barExtraLength-newLength);
barExtraLength=newLength;
}
}else{
pausedTimeWithoutGrowing+=deltaTimeInMilliSeconds;
}
}
pausedTimeWithoutGrowing 显而易见pausedTimeWithoutGrowing作为圆弧在最大/最小角度保持的时间,初始状态下默认值是0,那么代码逻辑走入else: pausedTimeWithoutGrowing+=deltaTimeInMilliSeconds。 调用了updateBarLength,应该说现在圆弧还处在角度保持不变的状态下,但是圆弧的起始绘制角度却一刻不能停: //spinSpeed作为每秒转过的角度,作为每秒的角度速度,用当前的时间差deltaTime计算,在这段时间里面转过了多少角度 floatdeltaNormalized=deltaTime*spinSpeed/1000.0f; //mProgress永远处于递增的状态 mProgress+=deltaNormalized; //当mProgress超过360即超过一圈,那么减去一圈的角度360。 另外当超过一圈的时机,会给回调函数传入一个特殊的值-1,至于回调函数的具体处理逻辑,这个就看项目需求了。 if(mProgress>360){ mProgress-=360f; //Afullturnhasbeencompleted //werunthecallbackwith-1incasewewantto //doing,likechangingthecolor runCallback(-1.0f); } //更新lastTimeAnimated上次绘制时间 lastTimeAnimated=SystemClock.uptimeMillis(); //其实这一步刚刚开始看得有点迷惑性,后来一想,圆弧的0度起始点其实是从右半水平半径开始的,减去90度后,就会从圆的顶点开始。 floatfrom=mProgress-90; //barExtraLength在这个时段barExtraLength并没有被赋值那么,length的值一直都是16 floatlength=barLength+barExtraLength; //isInEditMode这个其实是为了修饰的代码,如果你添加在了xml里面,那么圆弧的默认显示状态就是0-135。 这个没什么好说的 if(isInEditMode()){ from=0; length=135; } //最后一步绘制 canvas.drawArc(circleBounds,from,length,false,barPaint); 那么以上的分析和注释都是在pausedTimeWithoutGrowing pausedTimeWithoutGrowing>=pauseGrowingTime //timeStartGrowing从圆弧开始变化,记录下变化的总时间, timeStartGrowing+=deltaTimeInMilliSeconds; /** *这个判断可以先跳过,看完函数后面的代码,再来看,我感觉会清晰点,当然如果你能理解,那就直接看 *barSpinCycleTime作为递增/递减规定的变化总是时间, *当timeStartGrowing大于这个时间时,说明圆弧的下一个变化就需要有所改变了 *所谓的改变就是递增->递减或者递减->递增 *这个改变的标记通过改变barGrowingFromFront来实现! barGrowingFromFront。 *于此同是将相关参数重新初始化pausedTimeWithoutGrowing *pausedTimeWithoutGrowing=0,保证了最大/最小状态下圆弧不会瞬间改变,而是有一个旋转并保持的过程 *并且为了精确计算圆弧的长度,还需要计算timeStartGrowing-=barSpinCycleTime *timeStartGrowing: 源码中barSpinCycleTime为460ms,如果timeStartGrowing=480ms,也就是说圆弧的角度不应该是16,而应该是16+(480-460)这段时间差对应的额外的角度 * */ if(timeStartGrowing>barSpinCycleTime){ //Wecompletedasizechangecycle //(growingorinking) timeStartGrowing-=barSpinCycleTime; //if(barGrowingFromFront){ pausedTimeWithoutGrowing=0; //} barGrowingFromFront=! barGrowingFromFront; } /** *在准备的环节,已经介绍过了,这是开始与结束慢,中间加速的函数 *timeStartGrowing/barSpinCycleTime其实就是计算当前变化的总时间,占规定变化时间上限的比列 *其实我做过实验,即便写成floatdistance=timeStartGrowing/barSpinCycleTime;如果你不是拥有像素眼,那么基本是看不出来的,毕竟整个变化时间才460ms,更不要说中间那段加速的时间了... */ floatdistance= (float)Math.cos((timeStartGrowing/barSpinCycleTime+1)*Math.PI)/2+0.5f; /** *如果不考虑自己重新订制圆弧的最大角度和最小角度,那么destLength其实是个常数, *这种只需要进行一次的计算,我建议可以直接在onSizeChanged里面做,而不用每次都计算一次,个人优化建议 *destLength代表着额外需要绘制的角度barExtraLength的最大值 */ floatdestLength=(barMaxLength-barLength); if(barGrowingFromFront){ /** *barGrowingFromFront的默认值就是true,也就是默认情况下是递增的 *之前的分析已经说过了floatlength=barLength+barExtraLength,绘制的角度,其实就是最小角度+额外需要绘制的角度。 *barExtraLength=时间(其实也可以理解为角度)的百分比*barExtraLength的最大值,这样就可以得出额外需要绘制的角度了 */ barExtraLength=distance*destLength; }else{ /** *当barGrowingFromFront为false,也就是圆弧已经处于最大角度的状态了,递减! *newLength计算的就是需要绘制的额外角度。 *例: 当前已经是270度了,时间比例是50%,递减的状态下,额外的角度=最大角度-已经过去的百分比数值。 *并且这里需要特别注意的是mProgress的起始点也必须不断改变,递增已经过去的百分比数值,否则无法做成首尾同步的协调递减变化效果。 (这个真不知道怎么说,如果不太明白的同学可以把第二行注释掉,你就可以看到那个奇葩的效果。 ) */ floatnewLength=destLength*(1-distance); mProgress+=(barExtraLength-newLength); barExtraLength=newLength; } 到这里,其实基本说完了,onDraw中绘制动效的代码在比较简单的状态的分析过了,不过这里,还需要强调下 mProgress+=deltaNormalized,这行代码的重要性。 其实我开篇动效的第二个控件,就是在改变了一些时间的值之后,把这行注释掉了,这一行保证了起始点的不断更新。 至关重要。 疑惑 整个控件读下来,也花了我好几天的时间,主要是参数有点多,还有纠结与一些数学公式。 其实直到现在,我还是有个地方不是很明白: //linearProgress代表着线性变化的标记。 if(! linearProgress){ floatfactor=2.0f; offset=(float)(1.0f-Math.pow(1.0f-mPro
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Materialish Progress 源码阅读 源码 阅读