來張簡單的效果圖:
代碼均在:ListVideoPlay
問題引出
半年前開始接觸Android列表播放短視頻開發是從 VideoPlayerManager 這里開始的。
該庫核心思路:
1.使用
TextureView
嵌套在Item View里面作為視頻渲染
2.將視頻的生命周期全部在子線程隊列執行,然后post到主線程,避免ANR以及周期混亂
對此在Medium上作者做出了解釋
但存在一個棘手的問題:
由于TextureView是在ItemView中,所有壓根就不能無痕全屏切換播放
但是對于國內的視頻播放app來說,敢問哪家不要求全屏播放?
于是想出了一個方案就是在ListView
或者RecyclerView
布局同等層級中加入視頻播放的布局,將播放布局依附在itemView的顯示區域,然后監聽ListView
或RecyclerView
的滾動,通過滾動的位置來動態移動視頻播放布局的位置,這樣在全屏只需要將出了ListView
或RecyclerView
外的其他布局全部隱藏,將視頻播放的播放Resize到屏幕的寬高。大致的代碼可以在ListViewFragment這里看到.
BTW:就我所看到的,敢打賭目前市面上大部分的所謂的列表播放視頻思路大致如此
上述雖然能實現功能,但是在維護上存在極大的問題,無論是ListView
或RecyclerView
各種詭異滾動問題以及ViewPager
相關的滾動問題, 還是每次新建一個頁面都要將視頻所有的布局寫死在xml文件中。完全無任何擴展性可言。并且當需要在點擊視頻時進入詳情頁? 落地頁 ?做無痕,不斷播的交互簡直是天方夜譚。
既然思路沒問題,那就著手優化重構了。其實只要找到問題的關鍵點:
- 視頻布局真的就必須固定在某個頁面布局中?
2.視頻布局每次滾動都會有一個可依附的View
(粘在上面,看著就像內嵌在列表里面一樣),必須要監聽ListView
,ViewPager
的滾動?
3.視頻布局無論是橫豎屏或者進入詳情頁的視頻布局應該都必須是同一個才能保證無痕播放
橫豎屏保證是同一個尚好處理,但是當需要切換界面的時怎樣保證是同一個?
解決方案
一個Activity擁有一個頂層的View 即:
(ViewGroup) mContext.getWindow().getDecorView()
該decorView能保證隨時隨地都能附在activity的最頂層,將視頻播放相關的所有布局都置于其中就能解決問題1和3 存在的問題
但當將布局Add到DecorView后,會導致任何時候視頻布局都會在頂層,會遮擋StatusBar
,ActionBar
或者其他滾動列表之外的布局,此時就需要將視頻的播放布局嵌套在一個FrameLayout
中,如 VideoLayerView.java#L47,當視頻整體的布局是在合理的范圍(指滾動列表在整個屏幕的繪制區域Rect) 滾動此FrameLayout
, 當在非合理范圍內就滾動真正的視頻布局, 這樣就完美的將視頻播放的布局 Attach
在了列表item上跟隨滾動了
Android在ViewTreeObserver
中添加了每個View
,改View
的相關變化都會在ViewTreeObserver
回調,比如說:
mTrackView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
@Override
public void onScrollChanged() {
//View滾動時會回調
}
});
mTrackView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
//重新layout會被回調
}
});
在此只需要關心ViewTreeObserver.OnScrollChangedListener
即可,這樣就根本不用關心ListView
,RecyclerView
,ViewPager
的滾動距離來改變視頻布局的位置了,具體代碼可在ViewTracker.java#L202 查看到
因此根據上述思路簡單設計出了 一個View 粘附到 另一個View上 且跟隨滾動的框架
詳情見 ViewTracker
放張效果圖:
Demo DetailFragment
只要將跟隨滾動的邏輯處理ok,視頻播放相關的可以將播放布局參照demo來進行整合就能實現無縫播放