SpiralDev
SpiralDev

Reputation: 7321

How to prevent loading all Tabs in Tablayout with Viewpager2 during initialization via TabLayoutMediator?

I have a Viewpager2 set up with Tablyout via TabLayoutMediator. Adapter for the Viewpager2 is RecyclerView.Adapter

I am initializing Viewpager2 and Tablayout, and loading image into tabs as below:

    myViewPager.apply {
        offscreenPageLimit = 6
        adapter = myRecycerViewAdapter
    }

    TabLayoutMediator(myTabLayout, myViewPager) { tab, position ->
        val tabView: View =
        LayoutInflater.from(context).inflate(R.layout.tab_magazine_preview, null)

         Glide.with(context)
        .load(urls[position])
        .into(tabView.imageView)

        tab.customView = tabView
    }.attach()

Now this implementation creates all tabs during initializing and loads all urls into imageview in tabs. This is resource consuming approach as user might not swipe to all the way till the end. And If I have huge list of urls, this is even more resource consuming.

There is nice feature of Viewpager2 - offscreenPageLimit which helps to load only certain amount of adjacent pages. Can I have similar feature for Tablayout?

I want to load only ceratin amount of adjacent tabs at time as user swipes.

Upvotes: 0

Views: 1086

Answers (1)

artman
artman

Reputation: 641

You can detect swipe gestures on TabLayout using View.OnScrollChangeListener and then load images, but quick test showed me that you need to load images manually on initialization for visible tabs, because there was no scrolling.

tabLayout.setOnScrollChangeListener { v, scrollX, scrollY, oldScrollX, oldScrollY ->
    if (v is TabLayout) {
        val scrollBounds = Rect()
        v.getHitRect(scrollBounds)
        for (i in 0 until v.tabCount) {
            if (v.getTabAt(i)!!.view.getLocalVisibleRect(scrollBounds)) {
                Log.i("!!!!!!!!!!", "child visible at: "  + i);
                // check if image is not loaded/loading and load
            } else {
                Log.i("!!!!!!!!!!", "child invisible: "  + i);
            }
        }
    }
}

OLD ANSWER. I misunderstood the question:

You can do that by listening for ViewPager2.OnPageChangeCallback and telling desired fragments to load their images based on page position.

Make sure your page fragment implements interface that allows it to load image:

interface ILoadImage {
    fun loadImage()
}

class MyPageFragment : Fragment(), ILoadImage {
    private var isImageLoaded = false
    
    override fun loadImage() {
        if (isImageLoaded) return
        /*
         ... load image
         */
        isImageLoaded = true
    }
}

Then inside your myRecycerViewAdapter create function that will accept selected page position and update fragments

class MyRecycerViewAdapter /* ... */ {
    val fragments: List<ILoadImage> = //...
    
    fun loadImagesForPosition(position: Int) {
        fragments[position].loadImage()
        // also you can load images for next or prev fragments
    }
}

And finally listen for OnPageChangedCallback and tell adapter to update fragments

myViewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
    override fun onPageSelected(position: Int) {
        super.onPageSelected(position)
        myRecycerViewAdapter.loadImagesForPosition(position)
    }
})

Upvotes: 1

Related Questions