Android technology sharing ViewPager2 off screen loading, to achieve tiktok video slide

Keywords: Android Design Pattern viewpager

A tiktok effect similar to the small and small sliding of the video is needed. So called preloading,

When the current page is displayed, the content behind the current page needs to be loaded in advance to ensure that the user slides to the next video,

It can reduce the waiting time and browse smoothly at normal network speed.

In the fourth version of ViewPager2, a new feature has been added: setOffscreenPageLimit

setOffscreenPageLimit(int limit)

Parameter: limit how many pages will remain off the screen on either side. Valid values are > = 1 and the default value is - 1

Note: set the number of pages that should remain on either side of the currently visible page. Pages that exceed this limit will be recreated from the adapter when needed. The value set must be greater than 0 or the default value - 1. The front and back (limit number) pages of the current page will be added to the view hierarchy. Even if it is invisible, exceeding the limit number will be deleted from the view, but will be recycled like RecyclerView.

Through the method description, we can know that this feature is an artifact for this function, but can it achieve the expected effect.

We simply write a Demo to test the life cycle of the View when sliding, so that we can create it in the appropriate callback method. Destroy the player and pause / play the video.

use:

dependencies {
    implementation("androidx.viewpager2:viewpager2:1.0.0")
}

Adapter

 inner class VPAdapter() :
        RecyclerView.Adapter<VPAdapter.BaseViewHolder?>() {

        override fun onCreateViewHolder( parent: ViewGroup, viewType: Int): BaseViewHolder {
            val itemView: View = LayoutInflater.from(parent.context).inflate(R.layout.vp_item_layout, parent, false)
            return BaseViewHolder(itemView)
        }

        override fun onBindViewHolder( holder: BaseViewHolder, position: Int) {

        }

        override fun getItemCount(): Int {
            return dataArray.size
        }

        override fun onViewDetachedFromWindow(holder: BaseViewHolder) {
            super.onViewDetachedFromWindow(holder)
            Log.d("ViewPager2","View Off screen page ${holder.adapterPosition+1}page")
        }

        override fun onViewAttachedToWindow(holder: BaseViewHolder) {
            super.onViewAttachedToWindow(holder)
            Log.d("ViewPager2","View Join screen page ${holder.adapterPosition+1}page")
        }

        inner class BaseViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
            var root: RelativeLayout?
            var image :TextView?
            init {
                root = itemView.findViewById(R.id.root)
                image =  itemView.findViewById(R.id.iv_test)
            }
        }
    }

Prepare 10 pieces of data and set the offscreen pagelimit to 1, then the number of pages on the screen will always be 3. When the first page is expected, a second page will be created. When you slide to the second page, create a third page. When you slide to the third page, create the fourth page, and the first page goes to onViewDetachedFromWindow to delete it from the view. Next, let's slide and look at the log.

Enter page
ViewPager2: View Add screen page 1
ViewPager2: View Add screen page 2

Slide to second page
ViewPager2: View Add screen page 3

Slide to page 3
ViewPager2: View Add screen page 4
ViewPager2: View Off screen page 1

Slide to page 4
ViewPager2: View Add screen page 5
ViewPager2: View Off screen page 2

Return to page 3
ViewPager2: View Add screen page 2
ViewPager2: View Off screen page 5

It can be seen that the log input fully meets our expectations. Except after the first page, there will always be three pages on the view.

We just need to create the player in the corresponding callback, pause the playback and destroy the player. as follows

 stay View Join the callback of the window, create the player, start playing, and then pause⏸️
 override fun onViewAttachedToWindow(holder: BaseViewHolder) {
            super.onViewAttachedToWindow(holder)
            Log.d("ViewPager2","View Join screen page ${holder.adapterPosition+1}page")
            val player =createArLivePlayer()
            dataArray[holder.adapterPosition].player=player
         		player.startPlay("url")
         		player.pause()
        }
stay View In the callback leaving the window, destroy the player
override fun onViewDetachedFromWindow(holder: BaseViewHolder) {
            super.onViewDetachedFromWindow(holder)
           Log.d("ViewPager2","View Off screen page ${holder.adapterPosition+1}page")
           
            dataArray[holder.adapterPosition].player.release()
        }

Pause after creating the player, so that the player can buffer the video data of the next page. Then we need to find another place to start playing the player of the currently displayed page.

Just register the page turning monitor of ViewPager2, play the player of the current page, and pause the playback of other pages.

binding.vp.registerOnPageChangeCallback(object :ViewPager2.OnPageChangeCallback(){
            override fun onPageSelected(position: Int) {
                super.onPageSelected(position)
                dataArray.forEachIndexed { index, videoData ->
                    if (index==position){
                            dataArray[index].player?.resumePlay()
                            Log.d("ViewPager2","current ${index}play")
                        }

                    }else{
                            dataArray[index].player?.pause()
                             Log.d("ViewPager2","current ${index}suspend")
                        }
                    }
                }
            }
        })

After running, it is found that the effect is very silky, as shown in the figure. Therefore, ViewPager2 is very suitable for similar scenarios

Posted by bodge on Wed, 01 Dec 2021 00:44:10 -0800