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
)
}
)
}
}