2 years ago

#33604

test-img

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

Accepted video resources