類似抖音直播間滑動載入頁

天星技術團隊發表於2018-07-17

作者:蒼王 時間:2018.7.17

以下是我這個系列的相關文章,有興趣可以參考一下,可以給個喜歡或者關注我的文章。

[Android]如何做一個崩潰率少於千分之三噶應用app--章節列表

Android元件化架構熱賣中

現在很多直播軟體都具備無限迴圈載入的功能還有就是群聊圖片預覽,這節就分析一下,這種功能的實現。

1.ViewPager的製作無限迴圈

2.RecylerView滑動,使它可以定在其中的完整的一格

3.自定義View和Layout達到滑動效果。

先給大家看看ViewPager的做法吧。其至少需要4個View來完成物件複用

ViewPager複用.png

private fun initView() {
        // 初始化複用的預覽介面,4個足夠滿足滑動排程
        for (i in 0..3) {
            val previewView = MediaPreviewView(this)
            previewView.tag = i
            previewViews.add(previewView)
        }
       //自定義pagerAdapter
        mediaview_viewpager.adapter = object : PagerAdapter() {
            override fun instantiateItem(container: ViewGroup, position: Int): Any {
                Log.i(TAG, "View index = ${(position) % 4}, position = $position, current position = $currentPosition")
                val message = mediaItems[position]
                val previewView = previewViews[(position) %4]  //獲取複用的view
                // 設定媒體資料
                previewView.setMediaItem(message)
                if (previewView.parent == null) {  //新增View
                    container.addView(previewView)
                }
                return previewView
            }
            //清空View中的內容,以便複用
            override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
                val previewView = previewViews[(position) % 5]
                previewView.clear()
            }

            override fun isViewFromObject(view: View, `object`: Any): Boolean {
                return view == `object`
            }
            //獲取adapter的資料量
            override fun getCount() = mediaItems.size
            
            override fun getItemPosition(`object`: Any): Int {
                return if ((`object` as View).tag as Int == (currentPosition) % 5) {
                    POSITION_UNCHANGED
                } else {
                    POSITION_NONE
                }
            }
        }
        //新增滑動監聽
        mediaview_viewpager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
            override fun onPageScrollStateChanged(state: Int) {}

            override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}

            override fun onPageSelected(position: Int) {
                if (position != currentPosition) {
                    currentPosition = position
                    lastIndexId = mediaItems[position].indexId
                    if (position == 0) { //到第一個向前請求更多資料
                        getForwardMessages()
                    }
                    if (position == mediaItems.lastIndex) {  //到末尾向後請求更多資料
                        getBackwardMessages()
                    }
                }
            }
        })
    }
複製程式碼

這裡資料變更的時候,ViewPager會強制要求你重新整理介面。如果你滑動的時候在網路獲取資料地址,ViewPager強制重新整理,你就發現突然黑屏了一下。。。這體驗,真的非常差的。。。

Viewpager是沒提供區域性重新整理的方法的,所以這個bug也無法修復,除非你重寫整個Viewpager了,但是這樣也不利於後續優化維護。

如果滑動是迴圈且數量是確定的,可以採取這種方案,但是需要動態更新資料,就不能採取這種方案了,體驗很差。 直播滑動和線上圖片輪播是需要動態載入,所以此方案不適用。

2.使用RecylerView的卡位的方案,這裡非常簡單,只需要PagerSnapHelper直接關聯RecylerView,ReyclerView會直接完成View複用。而RecylerView擁有區域性重新整理的介面功能,所以不會出現閃屏的功能。

private fun initView() {
        mediaview_recycler.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
        mediaview_recycler.adapter = MediaViewAdapter()
        mediaview_recycler.isNestedScrollingEnabled = false
        //這裡簡單使用PagerSanpHepler關聯到RecylverView就可以了
        PagerSnapHelper().apply {
            attachToRecyclerView(mediaview_recycler)
        }
    }
複製程式碼

區域性向前重新整理

private fun getForwardMessages() {
          mediaItems.addAll(0, it)
          mediaview_recycler.adapter.notifyItemRangeInserted(0, it.size)
          mediaview_recycler.adapter.notifyItemRangeChanged(0, mediaItems.size)

    }
複製程式碼

區域性向後重新整理

private fun getBackwardMessages() {
         if (it.isNotEmpty()) {
                 mediaItems.addAll(it)
                 mediaview_recycler.adapter.notifyItemInserted(mediaItems.size - it.size)
         }
    }
複製程式碼

SnapHelper還有一個居中的效果的LinearSnapHelper

這裡如果還需要判斷視訊播放時機,需要複寫LayoutManager的方法。可以參考下面的文章 Android中模仿抖音的滑動RecycleView的實現

有這個PageRecylerView的開源庫,但是反射了使用了RecylerView的mViewFlinger物件,如果上線google play是無法使用的。

https://github.com/ckrgithub/PageRecyclerView

3.自定義layout 這個開源是大神五年前寫的,感覺就是ViewPager的原版。資料變更也需要強刷整個頁面,所以會有閃屏的問題,需要改寫重新整理頁面的方式為區域性重新整理。

https://github.com/castorflex/VerticalViewPager

……2018.7.17更新…… 1.如果播放器存在進度條,那麼recylerView就需要劃分一個區域,觸控事件向下傳遞,複寫攔截事件。

var canSmoothDownSide = false //true表示可以滑動,false表示不可以
    override fun onInterceptTouchEvent(e: MotionEvent?): Boolean {
        if (e?.action == MotionEvent.ACTION_MOVE && !canSmoothDownSide) {
            if (e.rawY > ScreenUtils.getScreenHeight(context) - ScreenUtils.dp2Px(context, 80f)) {
                return false
            }
        }
        return super.onInterceptTouchEvent(e)
    }
複製程式碼

2.使用Exoplayer播放,ExoPlayerView,播放(true)和暫停(false)都是使用setPlayWhenReady,如果你使用了stopVideo來暫停,當recylerView滑動後再劃回來會黑屏。一個ExoPlayerView,無法設定兩次prepare MediaSource作為替換,不然會顯示AudioFlinger could not create Task。

3.Exoplayer,如何視訊太短(1~3秒)恢復過來會有黑屏的問題。視訊有時候也會因為recylerView切換後無法卡在停止的那幀,無法播放。解決的方法是使用RecylerView onChildViewAttachedToWindow的時候,重新設定進度播放進度。

天星技術團QQ:557247785。

相關文章