独角兽企业重金招聘Python工程师标准>>>
如何做一个“流畅”而且“准确”的进度条?
流畅!由两个条件决定,更新的频率 和 更新的精度
频度和精度要相适应,才能保证流畅,并不是精度越高越好,也不是频度越高越好!我来说个例子,你们就明白了。500ms的更新频率和10刻度的精度,只是浪费系统资源去频繁更新而已,因为整个进度条只准你跳到10次 。反之5s的更新频率和1000刻度的精度只是让高精度怀才不遇而已,因为50s的曲子也就更新10次,1000刻度只相当于了10刻度。
为了让每一次更新都能合适的体现在刻度上,刻度的总长度要大于总更新频率。按照经验来看的话,对于一首5min歌一般1s一更新,300次更新适合的刻度应该在600-1000.
准确!如何衡量一个进度条是否准确呢?这要从以下两个方面来衡量:
准确拖拽:能够准确反应用户对进度条的拖拽,又不使用户感到困扰
准确更新:无论文件多长多短,进度条的更新,尤其是最后一次的更新,要能准确反应出歌曲长度。
这两个点大致一看貌似不是什么很难做的事情,但是其实也不是什么一拍脑袋就能搞好的事情。
第一条准确拖拽:准确的进度条拖拽,取决于一个很小时间参数。可拖拽进度条一般用seekBar实现,所以你的每一点点拖拽都将直接回调到onProgressChanged()。所以如果你对每一次回调都响应,并且去设置音乐位置的话,虽然可以很准确,但是用户体验并不好,用户耳朵里将听到乱七八糟的音乐快进或快退的声音。因此你必须适当的忽略一些回调。这个就由时间参数time来判定。
if ((now - mLastSeekEventTime) > time) {
mLastSeekEventTime = now;
//set music position
}
既然time太短影响用户体验,那我们就设置time长点比如time==700ms如何?太长的弊端就是会影响准确性,用户如果快速拖动进度条到松手的时候,如果时间短于700ms那么进度条会跳回到之前的位置,导致用户反感不已。那到底多少合适?经过多次的实验,大概在250ms左右是一个比较合理的值,又不会让用户耳朵过度受到骚扰,也不会影响用户的正常拖拽。
第二条准确更新,大多数程序员在写音乐的更新进度条时,习惯上会按1秒/次的频率去主动更新界面上的进度条,但是这个地方是有问题的。如果开始更新进度条之时正好是音乐开始播放之时,那么1秒/次是可以正好反映整个音乐从开始到结束的生命过程(以下简称生命过程)。但是这是一个理想的情况,现实是,永远不会有这么理想的情况发生。举例如下:
一首5s钟的音乐,当它开始播放的时候,由于程序运行自身会消耗时间的缘故,进度条的更新并不是和音乐同时开始的。从程序下命令开始播放音乐,到进度条开始更新极大的可能会有时间差(handler运行,service启动,各种逻辑判断等等造成的)。我测试的时候启动差值在200ms左右,图上举的例子是500ms的时间差。
简单点说就是进度条晚了500ms。所以,进度条的开始时间是进度条的第0秒,但是实际上确是音乐的第0.5秒。进度条的第二次更新是进度条的第1秒,但是却是音乐的第1.5秒。然后累加到最后,当进度条更新到第五次时,也就是进度条的第4秒时,其表示的是音乐的第4.5秒。然后在500毫秒之后,音乐播放完毕,通知进度条停止更新,进度条因此没来得及更新最后一次,就结束了,进度条最后停在了第4.5秒。
你所看到的现象就是进度条没有走完全程,音乐就停止了。为什么一定是5s左右?你大概猜到了原因,如果是正常音乐的话(正常都是5分钟左右),那500毫秒的误差在进度条上是根本体现不出来的(may肉眼不可见...)。
你至少可以在《酷狗音乐v4.1.7》上看到这种现象,前提是你要先找个5s左右的音乐文件。除了《多米音乐》可以准确播放外,其他的播放器(比如百度音乐,天天动听,QQ音乐)都不支持小文件的音乐播放所以无法测试(正好有意无意的避免了这个bug....)。
如何解决这个奇葩的无人关心的钻牛角尖的小问题呢?google给出了自己的答案。原生代码里在更新的时候有这样一段代码
long remaining = 1000 - (pos % 1000);这里的两个1000都代表1秒,pos代表当前播放音乐已经播放了的时间,单位毫秒。他们所计算出来的remaining是告诉进度条下一次更新的时间间隔是多少。
你可能已经发现了,通过remaining可以微调时间更新的频率,他会使得进度条的更新频率总是趋近于音乐播放的每一秒,而不是死板的遵循客观时间上的每一秒。这样的一个设置,可以有效的修正每一次的更新误差,这些误差产生于启动延时,程序计算,程序通信等等。举个例子,同样是之前的案例,当音乐刚启动,进度条在500ms后才开始更新,那么这次进度条的第二次更新就不再是死板的1秒钟了,而是1000-(50000)=500ms。进度条的第二次更新将会是在500ms之后,而不是1秒之后。这种修正,直接在最大程度上比秒了小音乐播放时的悲剧。我可以用数据说明问题:
每秒一次固定频率更新,我叫他A策略,google动态调整频率更新,我叫他B策略
可以看到A策略更新次数较少,而且在小文件时最后停止的千分比误差也比较大,文件越小越大。B策略更新次数较多,无论文件的大小如何,他都能够比较灵活的适应,最终千分比误差也比较小。应该说相当准确。