how am I able to get the current amount of items, that my PagingData<Product>
holds? The only solution I found was to call shopListAdapter.itemCount
but this always returns 0.
What I am trying to do is: Get the current amount of items that are currently displayed by my PagingAdapter, submit this value to my shopViewModel, and then use this value inside another fragment (DialogFragment). Or: Get the amount of items from my PagingData<Product>
inside my viewmodel
Here is my current approach
@AndroidEntryPoint
class ShopFragment : Fragment(R.layout.fragment_shop), ShopAdapter.OnItemClickListener {
private val shopViewModel: ShopViewModel by navGraphViewModels(R.id.nav_shop) { defaultViewModelProviderFactory }
private var _shopBinding: FragmentShopBinding? = null
private val shopBinding: FragmentShopBinding get() = _shopBinding!!
@Inject lateinit var shopListAdapter: ShopAdapter
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_shopBinding = FragmentShopBinding.inflate(inflater, container, false)
return shopBinding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
collectShopList()
}
private fun collectShopList(){
shopViewModel.shopPagingData.observe(viewLifecycleOwner) {
shopListAdapter.submitData(viewLifecycleOwner.lifecycle, it)
shopViewModel.setItemAmount(shopListAdapter.itemCount)
}
}
}
@AndroidEntryPoint
class ShopFilterFragment : DialogFragment() {
private val shopViewModel: ShopViewModel by navGraphViewModels(R.id.nav_shop) { defaultViewModelProviderFactory }
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_shop_filter, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
shopViewModel.itemAmount.observe(viewLifecycleOwner) {
toast(it.toString()) <-- Always displays 0
}
}
}
class ShopViewModel @ViewModelInject constructor(
private val shopRepository: ShopRepository
) : ViewModel() {
private val _itemAmount = MutableLiveData<Int>()
val itemAmount: LiveData<Int> get() = _itemAmount
private val query = MutableLiveData(QueryHolder(null, null, null))
val shopPagingData = query.switchMap { query -> shopRepository.search(query).cachedIn(viewModelScope) }
fun search(newQuery: QueryHolder) {
query.value = newQuery
}
fun setItemAmount(amount: Int) {
_itemAmount.value = amount
}
}
adapter.itemCount
is the correct way, but note that there are possibilities for it to race with what the adapter has received (page will load and then notify adapter - if you call .itemCount
between those steps it will return the incorrect number).
To wait for adapter to present the page, the easiest way is to use loadStateListener
/ loadStateFlow
, as LoadState
updates are guaranteed to be synchronous with adapter events.
shopListAdapter.addLoadStateListener { combinedLoadStates ->
// If you don't want to call all the time, you
// can filter on changes in combinedLoadStates
shopViewModel.setItemAmount(shopListAdapter.itemCount)
}
Don't forget to unregister in case of leaks.
Alternatively, you can use flow based approach:
.. onViewCreated(..) {
viewLifecycleOwner.lifecycleScope.launch {
shopListAdapter.loadStateFlow.collect { combinedLoadStates ->
// If you don't want to call all the time, you
// can filter on changes in combinedLoadStates
shopViewModel.setItemAmount(shopListAdapter.itemCount)
}
}
}