1

I was trying to display Two sets of Product List in ViewPager tabs inside a fragment. I was also using Paging library. Able to get data loaded and pagination is also working fine. I was also able to invalidate and reload paged list data, if I am in same fragment.

On moving to another fragment from this Tab layout and return to this fragment. I am able to see old data in both the tabs. But reload/pagination is not happening. Afer Invalidating the data, the fetched new data is not shown. The recycler view contains old data. I am getting the data from API and execute submit list - Diff Utils is also getting called. But the ProductPagedListAdapter - recycler view methods are not called.

I feel on recreating the fragment adapter is recreating but it is not getting bound to existing recycler view. I tried retainInstance, it was not working. I am using the two different instances of same ProductFragment in view pager. Due to some issues I am observing data from Parent Fragment ProductTabFragment and sending data to ViewPager fragments.

Any help on this would be highly appreciated.

ViewPager Fragment

 class ProductTabFragment : Fragment() {

    private lateinit var tabLayout: TabLayout
    private lateinit var viewPager: ViewPager2

    @Inject
    lateinit var viewModelFactory: ViewModelProvider.Factory
    private val viewModel: ProductViewModel by viewModels(
        ownerProducer = { activity as FragmentActivity },
        factoryProducer = { viewModelFactory }
    )
  

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.product_tab_fragment, container, false)
        tabLayout = view.findViewById(R.id.tabLayoutProduct)
        viewPager = view.findViewById(R.id.viewPagerLayoutProduct)

        return view
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
  

        viewPager.apply {
            adapter = ProductAdapter(childFragmentManager, viewLifecycleOwner.lifecycle, viewModel.getTabCount())
            registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
                override fun onPageSelected(position: Int) {
                    viewModel.selectedTab = position
                }
            })


        TabLayoutMediator(tabLayout, viewPager) { tab, position ->
            tab.text = when (position) {
                POSITION_TAB_RECOMMENDED -> TAB_TITLE_RECOMMENDED
                POSITION_TAB_RECENT -> TAB_TITLE_RECENT
                else -> TAB_TITLE_RECOMMENDED
            }
        }.attach()
        }

 
        viewModel.recommendedList.observe(viewLifecycleOwner, Observer {
            sendDataToFragment(POSITION_TAB_RECOMMENDED, it)
        })

        viewModel.recentList.observe(viewLifecycleOwner, Observer {
            sendDataToFragment(POSITION_TAB_RECENT, it)
        })

    }

    override fun onAttach(context: Context) {
        super.onAttach(context)
        retainInstance = true
    }

    private fun sendDataToFragment(fragmentPosition: Int, productList: PagedList<Product>?) {
        val mFragment: Fragment? =
            (viewPager.adapter as? ProductTabPagerAdapter)?.tabFragments?.get(fragmentPosition)
        mFragment?.let { (it as? ProductFragment)?.setUpListData(productList) }
    }



    companion object {        
        const val POSITION_TAB_RECOMMENDED = 0
        const val POSITION_TAB_RECENT = 1
        private const val TAB_TITLE_RECOMMENDED = "RECOMMENDED"
        private const val TAB_TITLE_RECENT = "RECENT"

        @JvmStatic
        fun newInstance() = ProductTabFragment()
    }
}

Pager Adapter

    class ProductTabPagerAdapter(fragmentManager: FragmentManager,
                                 lifecycle: Lifecycle, val tabCount :Int) :
    FragmentStateAdapter(fragmentManager,lifecycle) {

    internal var tabFragments = listOf<Fragment>(ProductFragment.newInstance(ProductTabFragment.POSITION_TAB_RECOMMENDED),ProductFragment.newInstance(ProductTabFragment.POSITION_TAB_RECENT))

    override fun getItemCount(): Int {
        return tabCount
    }

    override fun createFragment(position: Int): Fragment {
        return tabFragments[position]
    }

}

**Same Reusable fragments used in ViewPager**

class ProductFragment : Fragment() {

    private var productAdapter = ProductPagedListAdapter()

  
    @Inject
    lateinit var viewModelFactory: ViewModelProvider.Factory
    private val viewModel: ProductViewModel by viewModels(
        ownerProducer = { activity as FragmentActivity },
        factoryProducer = { viewModelFactory }
    )


    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? = inflater.inflate(R.layout.productÆ’_fragment, container, false)?.apply {
        initialProgressBar = findViewById(R.id.initialLoadProgressBar)
        loadMoreProgressBar = findViewById(R.id.loadMoreProgressBar)
    }


    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        view.findViewById<RecyclerView>(R.id.productList)?.apply {
            context?.also { ctx ->
                layoutManager = LinearLayoutManager(ctx)
            }
            adapter = productAdapter
        }
    }


    fun setUpListData(data: PagedList<Product>?) {
       productAdapter.submitList(data)
    }

    override fun onAttach(context: Context) {
        super.onAttach(context)
        retainInstance = true
    }
}

Paged List Adapater

  class ProductPagedListAdapter() : PagedListAdapter<Product, ProductItemHolder>(diffUtilCallBack) {


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductItemHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.product_item, parent, false)
        return ProductItemHolder(view)
    }

    override fun onBindViewHolder(holder: ProductItemHolder, position: Int) {
        getItem(position)?.let { holder.bind(it) }
    }
 companion object {
        var diffUtilCallBack = object : DiffUtil.ItemCallback<Product>() {

            override fun areItemsTheSame(
                oldItem: Product,
                newItem: Product
            )  = oldItem.id == newItem.id

            override fun areContentsTheSame(
                oldItem: Product,
                newItem: Product
            )= oldItem == newItem
              
        }
    }
}

View Model

    class ProductViewModel()) : ViewModel() {

    var recommendedList : LiveData<PagedList<Product>> = MutableLiveData<PagedList<Product>>()
    var recentList : LiveData<PagedList<Product>> = MutableLiveData<PagedList<Product>>()

    
    init {
        fetchRecentProducts()
        fetchRecommended()
    }

  
    fun fetchRecommended() {

        val pagedListConfig = PagedList.Config.Builder()
            .setPageSize(ProductDataSource.PAGE_LIMIT)
            .setEnablePlaceholders(false).build()

        val dataSourceFactory = object : DataSource.Factory<Int, Product>() {
            override fun create(): DataSource<Int, Product> {
                return ProductDataSource(
                    type = "recommended",
                )
            }
        }

        recommendedList = LivePagedListBuilder<Int, Product>(
            dataSourceFactory,
            pagedListConfig
        ).build()
    }

  
    fun fetchRecent() {

        val pagedListConfig = PagedList.Config.Builder()
            .setPageSize(ProductDataSource.PAGE_LIMIT)
            .setEnablePlaceholders(false).build()

        val dataSourceFactory = object : DataSource.Factory<Int, Product>() {
            override fun create(): DataSource<Int, Product> {
                return ProductDataSource(
                    type = "recent",
                )
            }
        }

        recentList = LivePagedListBuilder<Int, Product>(
            dataSourceFactory,
            pagedListConfig
        ).build()
    }


    fun resetPaginationData() {

        recommendedList.value?.dataSource?.invalidate()
        recentList.value?.dataSource?.invalidate()
    }

}

Data Source for paged List

   class ProductDataSource(
    private val type: String) : PageKeyedDataSource<Int, Product>() {

    override fun loadInitial(
        params: LoadInitialParams<Int>,
        callback: LoadInitialCallback<Int, Product>
    ) {

        compositeDisposable.add(
            productRepo.getProductList(
                tyoe
            ).subscribe(
                { productList ->
                    callback.onResult(
                        productList, null, PAGE_LIMIT
                    )
                }
        )
    }
}
Shay Kin
  • 2,539
  • 3
  • 15
  • 22
GGH
  • 11
  • 4

0 Answers0