android音乐播放器进度条研究

如何做一个“流畅”而且“准确”的进度条?

  • 流畅!由两个条件决定,更新的频率 和 更新的精度

频度和精度要相适应,才能保证流畅,并不是精度越高越好,也不是频度越高越好!我来说个例子,你们就明白了。500ms的更新频率和10刻度的精度,只是浪费系统资源去频繁更新而已,因为整个进度条只准你跳到10次 。反之5s的更新频率和1000刻度的精度只是让高精度怀才不遇而已,因为50s的曲子也就更新10次,1000刻度只相当于了10刻度。

为了让每一次更新都能合适的体现在刻度上,刻度的总长度要大于总更新频率。按照经验来看的话,对于一首5min歌一般1s一更新,300次更新适合的刻度应该在600-1000.

 

  • 准确!如何衡量一个进度条是否准确呢?这要从以下两个方面来衡量:
  1. 准确拖拽:能够准确反应用户对进度条的拖拽,又不使用户感到困扰
  2. 准确更新:无论文件多长多短,进度条的更新,尤其是最后一次的更新,要能准确反应出歌曲长度。
这两个点大致一看貌似不是什么很难做的事情,但是其实也不是什么一拍脑袋就能搞好的事情。
   第一条准确拖拽:准确的进度条拖拽,取决于一个很小时间参数。可拖拽进度条一般用seekBar实现,所以你的每一点点拖拽都将直接回调到onProgressChanged()。所以如果你对每一次回调都响应,并且去设置音乐位置的话,虽然可以很准确,但是用户体验并不好,用户耳朵里将听到乱七八糟的音乐快进或快退的声音。因此你必须适当的忽略一些回调。这个就由时间参数time来判定。
            if ((now - mLastSeekEventTime) > time) {
                mLastSeekEventTime = now;
                //set music position
             }
     既然time太短影响用户体验,那我们就设置time长点比如time==700ms如何?太长的弊端就是会影响准确性,用户如果快速拖动进度条到松手的时候,如果时间短于700ms那么进度条会跳回到之前的位置,导致用户反感不已。那到底多少合适?经过多次的实验,大概在250ms左右是一个比较合理的值,又不会让用户耳朵过度受到骚扰,也不会影响用户的正常拖拽。

   第二条准确更新,大多数程序员在写音乐的更新进度条时,习惯上会按1秒/次的频率去主动更新界面上的进度条,但是这个地方是有问题的。如果开始更新进度条之时正好是音乐开始播放之时,那么1秒/次是可以正好反映整个音乐从开始到结束的生命过程(以下简称生命过程)。但是这是一个理想的情况,现实是,永远不会有这么理想的情况发生。举例如下:
android音乐播放器进度条研究

  一首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策略
android音乐播放器进度条研究

可以看到A策略更新次数较少,而且在小文件时最后停止的千分比误差也比较大,文件越小越大。B策略更新次数较多,无论文件的大小如何,他都能够比较灵活的适应,最终千分比误差也比较小。应该说相当准确。
发布了58 篇原创文章 · 获赞 16 · 访问量 15万+
展开阅读全文

android 视屏播放器 全屏显示进度条 并自动隐藏的问题

11-21

编写了一个播放器,全屏播放,触摸屏幕,显示下方的进度条和控制按钮(默认情况下自动隐藏)这个已经编写成功了。如图 ![图片说明](https://img-ask.csdn.net/upload/201611/21/1479732903_449209.jpg) 但是我现在也想弄一个标题栏显示播放的视频名字,默认情况下隐藏,触摸屏幕时和下方的进度条等一起显示出来,然后出现问题了,上方的标题栏触摸屏幕显示不出来。 部分代码如下 ``` //这个是我上方的标题栏 <LinearLayout android:id="@+id/ll_theme" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:orientation="horizontal" android:visibility="invisible"> <TextView android:id="@+id/tv_theme" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="文件标题"/> </LinearLayout> <SurfaceView android:id="@+id/sv_player" android:layout_width="wrap_content" android:layout_height="wrap_content"/> //下方的进度条等 <LinearLayout android:id="@+id/ll_ctrl" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:gravity="center_vertical" android:orientation="horizontal" android:visibility="invisible"> <ImageButton android:id="@+id/ib_play" android:layout_width="45dp" android:layout_height="45dp" android:onClick="play" android:src="@drawable/ic_action_playback_pause"/> <ImageButton android:id="@+id/ib_forw" android:layout_width="45dp" android:layout_height="45dp" android:onClick="forw" android:src="@drawable/ic_action_playback_forw"/> <SeekBar android:id="@+id/sb" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:max="255"/> </LinearLayout> ``` ```触摸事件 public boolean onTouchEvent(MotionEvent event) { if(event.getAction()==MotionEvent.ACTION_DOWN) { ll_ctrl.setVisibility(View.VISIBLE); ll_theme.setVisibility(View.VISIBLE); new Thread() { public void run() { SystemClock.sleep(3000); runOnUiThread(new Runnable() { @Override public void run() { ll_ctrl.setVisibility(View.INVISIBLE); ll_theme.setVisibility(View.INVISIBLE); } }); } }.start(); } return super.onTouchEvent(event); } ``` 现在怀疑是布局文件出现错误。。请各位大神指点。。。 问答

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览