Android 高仿騰訊新聞視訊切換效果

zzh12138發表於2019-04-30
是的,你沒看錯,又是騰訊視訊!又是高仿!沒辦法啊,產(傻)品(子)喜歡抄襲別人,天下應用一大抄啊。

我能怎麼辦,我也很絕望啊

還是上效果圖吧(渣渣千元機,有點卡)

騰訊新聞效果

騰訊
同樣是新聞客戶端,為什麼你這麼優秀。

12.jpg
好了,吐槽完畢,看看demo的效果。
高仿效果
跟原效果還是有點差別的。
畢竟我們這些開發仔,怎麼跟大佬比呢

拆解一下頁面效果吧:

  • 新聞頁面視訊滾動到螢幕中間自動播放(wifi下,demo沒有判斷)
  • 點選視訊,頁面切換到視訊列表頁面,期間視訊不會停止播放,平滑過渡到第二個頁面
  • 視訊列表頁面滑動後,播放第一個完全可見的視訊(個人理解,理解錯了大佬不要打我)
  • 視訊列表頁面,視訊播放時,點選評論數,切換到評論頁面,效果同上
  • 視訊列表頁面,視訊播放時,點選其他item任意區域,該item滑到頂部,並播放視訊

無縫續播這裡不做講解(其實我也不會呀...),做過播放器的應該都懂吧,原理大致就是:

解碼器動態關聯不同的渲染檢視(RenderView),比如使用MediaPlayer動態關聯SurfaceView,就如同一個電腦主機不斷連線不同的顯示器。
複製程式碼

PS:這裡注意一下,render不要重置,不然會閃屏哦

Demo中用的是這個播放器 PlayerBase,高度解耦(不像其他播放器一樣,對佈局檔案有限制),支援各種自定義,最大的好處的就是提供能無縫續播助手(續播的話不要使用mediaPlayer,會出現問題)。

美滋滋

播放器相關

這裡還是簡單說下播放器怎麼用吧,不需要改介面的話(demo中略微做了修改),直接拷下面紅色方框中的類,在Application裡面進行配置就可以使用啦(ijk,exo需要引入對應的庫哦,demo中ijk不支援https,所以視訊可能不能播放)。

Android 高仿騰訊新聞視訊切換效果
無縫續播相關都在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切換的共享元素動畫,但實操之後發現效果不怎麼理想。
Android 高仿騰訊新聞視訊切換效果
效果如圖:
退出activity會閃屏
也試過,去掉系統的預設動畫,將主題設定為透明,自己手動平移,放縮view,還是有點問題(可能是我姿勢不對)。還是會有上面的閃屏問題。
攤手.jpg

沒辦法羅,只能在當前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版可能會有一點語法問題...)

Java_Demo

Kotlin_Demo

如需更多詳細的效果,可看下一篇部落格

相關文章