2 years ago
#33604
Radin
小ombine 3 LiveData into one with Paging 3
How can I combine 3 LiveData into one, I have a snippet in my project where I need to display the result of 3 different requests, into one RecyclerView, I read about it, and found out that it is possible to combine three LiveData into one, but I do not understand how this can be done using Paging 3.
Sample code in my ViewModel:
@HiltViewModel
class SearchViewModel @Inject constructor(
private val getAllCharacters: GetCharactersUseCase,
private val getAllLocations: GetLocationsUseCase,
private val getAllEpisodes: GetEpisodesUseCase
) : BaseViewModel() {
private val _charactersState = MutableLiveData<PagingData<RickAndMorty>>()
val charactersState: LiveData<PagingData<RickAndMorty>> = _charactersState
private val _locationsState = MutableLiveData<PagingData<RickAndMorty>>()
val locationsState: LiveData<PagingData<RickAndMorty>> = _locationsState
private val _episodesState = MutableLiveData<PagingData<RickAndMorty>>()
val episodesState: LiveData<PagingData<RickAndMorty>> = _episodesState
val liveDataMerger: MediatorLiveData<PagingData<RickAndMorty>> =
MediatorLiveData<PagingData<RickAndMorty>>()
fun merge() {
liveDataMerger.addSource(locationsState) {
liveDataMerger.value = it
}
liveDataMerger.addSource(charactersState) {
liveDataMerger.value = it
}
}
fun processAllRequests(
name: String
) {
viewModelScope.launch {
withContext(Dispatchers.IO) {
getAllCharacters(name, "", "")
}.collect {
_charactersState.value = it as PagingData<RickAndMorty>
}
}
viewModelScope.launch {
withContext(Dispatchers.IO) {
getAllLocations(name, "", "")
}.collect {
_locationsState.value = it as PagingData<RickAndMorty>
}
}
viewModelScope.launch {
withContext(Dispatchers.IO) {
getAllEpisodes(name, "")
}.collect {
_episodesState.value = it as PagingData<RickAndMorty>
}
}
}
}
functions getAllCharacters, getAllLocations, getAllEpisodes are suspend, and all my models are combined into one Sealed class.
example code in Fragment:
@AndroidEntryPoint
class SearchFragment : BaseFragment<FragmentSearchBinding, SearchViewModel>(
R.layout.fragment_search
) {
override val binding by viewBinding(FragmentSearchBinding::bind)
override val viewModel by viewModels<SearchViewModel>()
private val adapter: SearchAdapter = SearchAdapter()
private val args: SearchFragmentArgs by navArgs()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun initialize() {
setupRecycler()
}
private fun setupRecycler() = with(binding) {
searchRecycler.layoutManager = LinearLayoutManager(requireContext())
searchRecycler.adapter = adapter.withLoadStateFooter(LoadStateAdapter {
adapter.retry()
})
}
override fun setupRequests() {
viewModel.processAllRequests("")
Log.e("anime", args.filterType)
viewModel.merge()
}
override fun setupObserves() {
viewModel.liveDataMerger.observe(viewLifecycleOwner, {
lifecycleScope.launch {
adapter.submitData(it)
}
})
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.toolbar_menu, menu)
val searchItem = menu.findItem(R.id.search)
val searchView = searchItem.actionView as SearchView
searchView.setTools(context)
searchView.submitSearch { viewModel.processAllRequests(it.toString()) }
searchItem.setOnActionExpandListener(searchView) { hideKeyboard() }
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.filter) {
findNavController().navigate(
SearchFragmentDirections.actionSearchFragmentToFilterDialogFragment(
getString(R.string.search_filter_type)
)
)
}
return super.onOptionsItemSelected(item)
}
}
My Adapter:
class SearchAdapter :
PagingDataAdapter<RickAndMorty, SearchRecyclerViewHolder<ViewBinding>>(
SearchDiffUtil()
) {
class SearchDiffUtil : DiffUtil.ItemCallback<RickAndMorty>() {
override fun areItemsTheSame(oldItem: RickAndMorty, newItem: RickAndMorty): Boolean {
return oldItem == newItem
}
override fun areContentsTheSame(oldItem: RickAndMorty, newItem: RickAndMorty): Boolean {
return oldItem == newItem
}
}
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): SearchRecyclerViewHolder<ViewBinding> {
val inflater = LayoutInflater.from(parent.context)
return when (viewType) {
R.layout.item_characters -> SearchRecyclerViewHolder.CharactersViewHolder(
ItemCharactersBinding.inflate(
inflater,
parent,
false
)
)
R.layout.item_locations -> SearchRecyclerViewHolder.LocationsViewHolder(
ItemLocationsBinding.inflate(
inflater,
parent,
false
)
)
R.layout.item_episodes -> SearchRecyclerViewHolder.EpisodesViewHolder(
ItemEpisodesBinding.inflate(
inflater,
parent,
false
)
)
else -> throw IllegalAccessException("Invalid viewType provided")
}
}
override fun onBindViewHolder(
holder: SearchRecyclerViewHolder<ViewBinding>,
position: Int
) {
when (holder) {
is SearchRecyclerViewHolder.CharactersViewHolder ->
holder.onBind(getItem(position) as RickAndMorty.CharactersItem)
is SearchRecyclerViewHolder.LocationsViewHolder ->
holder.onBind(getItem(position) as RickAndMorty.LocationsItem)
is SearchRecyclerViewHolder.EpisodesViewHolder ->
holder.onBind(getItem(position) as RickAndMorty.EpisodesItem)
}
}
override fun getItemViewType(position: Int): Int {
return when (getItem(position)) {
is RickAndMorty.CharactersItem -> R.layout.item_characters
is RickAndMorty.LocationsItem -> R.layout.item_locations
is RickAndMorty.EpisodesItem -> R.layout.item_episodes
null -> throw IllegalStateException("Unknown view")
}
}
}
android
kotlin
kotlin-coroutines
android-livedata
android-paging-3
0 Answers
Your Answer