I have one fragment that instance that instantiates a view model injected by koin, the problem is that, one of the observed attributes are not stimulated in the fragment after the view model's postValue () action, it simply does not enter the method in the fragment even though it has been updated.
Fragement:
class ListFragment : Fragment() {
private lateinit var adapter: PostAdapter
private val viewModel: PostsViewModel by viewModel()
private var _binding: ListFragmentBinding? = null
private var defaultTopic = "news"
private var afterPage: String = ""
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.list_fragment, container, false)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = ListFragmentBinding.bind(view)
adapter = PostAdapter(mutableListOf(), requireContext()) {
val action = ListFragmentDirections.openDetailsFragment(it)
findNavController().navigate(action)
}
_binding?.let{
val llm = LinearLayoutManager(requireContext())
it.recyclerviewPosts.layoutManager = llm
it.recyclerviewPosts.adapter = adapter
recyclerViewListenerSetup(it, llm)
}
setupObservers()
}
private fun recyclerViewListenerSetup(it: ListFragmentBinding, llm: LinearLayoutManager) {
it.recyclerviewPosts.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val visibleItemCount: Int = llm.childCount
val totalItemCount: Int = llm.itemCount
val firstVisibleItemPosition: Int = llm.findFirstVisibleItemPosition()
if((visibleItemCount + firstVisibleItemPosition) >= totalItemCount
&& firstVisibleItemPosition >= 0) {
viewModel.getNextPage(defaultTopic, afterPage)
}
}
})
}
private fun setupObservers(){
viewModel.getPostList(defaultTopic)
viewModel.posts.observe(this as LifecycleOwner, { posts ->
if (posts.isSuccess && posts.getOrNull() != null) {
adapter.updateList(posts.getOrNull()!! as MutableList<PostsDTO>)
afterPage = posts.getOrNull()!![0].after
showList()
} else {
showError()
}
})
viewModel.loading.observe(this as LifecycleOwner, {
if (it) {
showLoading()
} else {
hideLoading()
}
})
viewModel.next.observe(this as LifecycleOwner, {
if (it.isSuccess && it.getOrNull() != null) {
adapter.addList(it.getOrNull() as MutableList<PostsDTO>)
afterPage = it.getOrNull()!![0].after
}
})
}
private fun showList(){
_binding?.let {
it.recyclerviewPosts.visibility = VISIBLE
}
}
private fun showLoading(){
_binding?.let {
it.loading.visibility = VISIBLE
it.containerError.root.visibility = GONE
it.recyclerviewPosts.visibility = GONE
}
}
private fun hideLoading(){
_binding?.let {
it.loading.visibility = GONE
}
}
private fun showError() {
_binding?.let {
it.containerError.root.visibility = VISIBLE
it.recyclerviewPosts.visibility = GONE
}
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_main, menu)
val searchItem = menu.findItem(R.id.action_search)
val searchManager =
requireActivity().getSystemService(Context.SEARCH_SERVICE) as SearchManager
val searchView = searchItem.actionView as SearchView
searchView.setSearchableInfo(searchManager.getSearchableInfo(activity?.componentName))
searchView.apply {
queryHint = "Search SubReddit"
setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
defaultTopic = query!!
viewModel.getPostList(defaultTopic)
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
return true
}
})
}
super.onCreateOptionsMenu(menu, inflater)
}
}
I try to call the viewmodel method to update the displayed list when the recyclerView scrolls.
ViewModel:
class PostsViewModel(private val repository: PostsRepository) : ViewModel(){
private val _posts = MutableLiveData<Result<List<PostsDTO>>>()
val posts: LiveData<Result<List<PostsDTO>>>
get() =_posts
private val _loading = MutableLiveData<Boolean>()
val loading: LiveData<Boolean>
get() = _loading
private val _next = MutableLiveData<Result<List<PostsDTO>>>()
val next: LiveData<Result<List<PostsDTO>>>
get() =_posts
fun getPostList(q: String){
viewModelScope.launch {
_loading.postValue(true)
repository.fetchPosts(q)
.collect {
_posts.value = it
}
_loading.postValue(false)
}
}
fun getNextPage(topic: String, afterPage: String) {
viewModelScope.launch {
repository.fetchNextPage(topic, afterPage)
.collect{
_next.postValue(it)
}
}
}
}
In this case after the request result of the next method has updated the viewmodel, the fragment is not stimulated in viewmodel.next.observer()