是的,你沒看錯,又是騰訊視訊!又是高仿!沒辦法啊,產(傻)品(子)喜歡抄襲別人,天下應用一大抄啊。
還是上效果圖吧(渣渣千元機,有點卡)
拆解一下頁面效果吧:
- 新聞頁面視訊滾動到螢幕中間自動播放(wifi下,demo沒有判斷)
- 點選視訊,頁面切換到視訊列表頁面,期間視訊不會停止播放,平滑過渡到第二個頁面
- 視訊列表頁面滑動後,播放第一個完全可見的視訊(個人理解,理解錯了大佬不要打我)
- 視訊列表頁面,視訊播放時,點選評論數,切換到評論頁面,效果同上
- 視訊列表頁面,視訊播放時,點選其他item任意區域,該item滑到頂部,並播放視訊
無縫續播這裡不做講解(其實我也不會呀...),做過播放器的應該都懂吧,原理大致就是:
解碼器動態關聯不同的渲染檢視(RenderView),比如使用MediaPlayer動態關聯SurfaceView,就如同一個電腦主機不斷連線不同的顯示器。
複製程式碼
PS:這裡注意一下,render不要重置,不然會閃屏哦
Demo中用的是這個播放器 PlayerBase,高度解耦(不像其他播放器一樣,對佈局檔案有限制),支援各種自定義,最大的好處的就是提供能無縫續播助手(續播的話不要使用mediaPlayer,會出現問題)。
播放器相關
這裡還是簡單說下播放器怎麼用吧,不需要改介面的話(demo中略微做了修改),直接拷下面紅色方框中的類,在Application裡面進行配置就可以使用啦(ijk,exo需要引入對應的庫哦,demo中ijk不支援https,所以視訊可能不能播放)。
無縫續播相關都在RelationAssist類裡。續播的話直接呼叫AssistPlayer.get().play(mContainer, null);
複製程式碼
就可以在不同的容器內(即上面的mContainer)繼續播放之前的內容了。 需要自定義介面或者有其他問題的參考這裡和demo,有問題的加群問群主吧。
新聞列表頁
用RecyclerView實現,這裡說一下頁面滑動對視訊item的處理:當頁面停止滑動後,判斷是螢幕中央是否有可見的視訊item,有則開始播放。同時,判斷之前是否有在播放的視訊滑出了介面,有則停止播放。
//虛擬碼
if (newState == SCROLL_STATE_IDLE) {
//滑動螢幕中間開始
LinearLayoutManager manager = (LinearLayoutManager) recyclerView.getLayoutManager();
int first = manager.findFirstVisibleItemPosition();
int last = manager.findLastVisibleItemPosition();
for (int i = first; i <= last; i++) {
if (isVideo) {
//列表視訊
if (isCenter && !AssistPlayer.get().isPlaying()) {
ImageView imageView = view.findViewById(R.id.adapter_video_image);
imageView.performClick();
break;
}
}
}
//滑出螢幕高度一半停止播放
int playPosition = mAdapter.getPlayPosition();
if (playPosition != -1) {
if (isOutSide) {
stopPlay();
}
}
複製程式碼
視訊列表頁
同樣也是RecyclerView實現,這裡滑動播放邏輯跟新聞列表頁有點不同(純屬個人理解,理解錯了各位大佬別打我)。進入頁面播放第一個視訊,上滑會載入更多(demo裡面沒有實現)。上下滑動停止後,會播放當前螢幕完全可見的第一個item的視訊。並且,當播放視訊時,點選其他item的任意位置,該item會滑動到頂部並播放視訊。當前視訊播放完畢後,如果列表頁後面還有視訊,會自動播放下一個,並將該item滑到頁面頂部。當退出視訊列表頁時,如果播放的不是第一個視訊,則不需要過渡動畫。 滑動監聽:
if (newState == SCROLL_STATE_IDLE) {
LinearLayoutManager manager = (LinearLayoutManager) recyclerView.getLayoutManager();
int first = manager.findFirstVisibleItemPosition();
int pos = manager.findFirstCompletelyVisibleItemPosition();
if (pos != mAdapter.getPlayPosition()) {
View view = mRecycler.getChildAt(pos - first);
ImageView imageView = view.findViewById(R.id.adapter_video_list_image);
imageView.performClick();
}
}
複製程式碼
因為已經對滑動進行了監聽,所以自動播放下一個視訊可以通過滑動item來實現:監聽視訊的播放事件,當視訊播放完畢後,判斷該視訊是否是最後一個,不是則滑到螢幕頂部,播放視訊。 PS:滑動要呼叫smoothScroll方法,不能直接調scroll。
評論頁
評論頁只是一個播放器容器和一個顯示評論資料的recyclerView(demo裡面沒有做評論蓋樓),這裡不細說,詳見demo。
視訊平滑過渡切換
重點來了,第一眼看到視訊平滑過渡切換頁面,想到的就是google在5.0版本提供activity切換的共享元素動畫,但實操之後發現效果不怎麼理想。 效果如圖: 也試過,去掉系統的預設動畫,將主題設定為透明,自己手動平移,放縮view,還是有點問題(可能是我姿勢不對)。還是會有上面的閃屏問題。沒辦法羅,只能在當前activity裡面進行過渡了。這裡我的做法是在根佈局新增一個fragment。這裡著重說一下要注意的幾個地方吧:
- 需要重寫onBackPressed(),區分是否橫屏模式,是否顯示了評論頁,列表視訊頁,還是普通新聞頁。
- 列表視訊頁,普通新聞頁都需要記錄下當前播放的item的position,退出頁面會用到。
- 列表視訊,評論頁請求回來的網路資料不能過早顯示。
- 過渡動畫需要在view繪製好後才開始執行。
下面是頁面過渡的過程:
- 獲取view的屬性(寬高、位置),新聞的資訊,並傳給fragment
- fragment背景設定為透明,改變關聯的view屬性,並開始動畫,還原背景色和view的屬性,這裡有個地方需要特別留意,如果你的播放容器跟我一樣是放在recyclerView裡面的,那麼位移的view必須是整個item!整個item!整個item!(子view總不可能跑出父view的範圍吧...)
- 動畫完畢後顯示載入的網路資料(按個人需求進行修改)
- 退出fragment的時候,將動畫反轉一遍就OK啦。要注意當前播放的視訊是否是進來播放的那個,是的話才需要執行動畫哦。
FragmentTransaction transaction = getFragmentManager().beginTransaction();
Bundle bundle = new Bundle();
bundle.putParcelable("attr", attr);
bundle.putParcelable("news", bean);
commentFragment.setArguments(bundle);
commentFragment.setOnCloseClickListener(this);
transaction.add(R.id.fragment_video_list_comment_container, commentFragment);
transaction.commit();
}
複製程式碼
mContainer.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
//繪製完畢,開始執行動畫
mContainer.getViewTreeObserver().removeOnPreDrawListener(this);
mContainer.getLocationOnScreen(location);
mContainer.setTranslationX(mAttr.getX() - location[0]);
mContainer.setTranslationY(mAttr.getY() - location[1]);
mContainer.setScaleX(mAttr.getWidth() / (float) mContainer.getMeasuredWidth());
mContainer.setScaleY(mAttr.getHeight() / (float) mContainer.getMeasuredHeight());
mRecycler.setAlpha(0);
mTextView.setAlpha(0);
mClose.setAlpha(0);
mCommentNum.setAlpha(0);
mContainer.animate().translationX(0).translationY(0).scaleX(1).scaleY(1).setDuration(DURATION);
mRecycler.animate().alpha(1).setDuration(DURATION);
mTextView.animate().alpha(1).setDuration(DURATION);
mClose.animate().alpha(1).setDuration(DURATION);
mCommentNum.animate().alpha(1).setDuration(DURATION);
AssistPlayer.get().play(mContainer, null);
return true;
}
});
複製程式碼
有疑問(播放器相關的點這裡)或者demo有bug的可以留言,看到會回覆的。
附上原始碼(Kotlin版可能會有一點語法問題...)
如需更多詳細的效果,可看下一篇部落格