3 votes

Les mises à jour des observables dans les données réelles ne fonctionnent pas dans les fragments.

J'ai un fragment qui instancie un modèle de vue injecté par Koin, le problème est que l'un des attributs observés n'est pas stimulé dans le fragment après l'action postValue () du modèle de vue, il n'entre tout simplement pas dans la méthode dans le fragment même s'il a été mis à jour.

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

}

J'essaie d'appeler la méthode viewmodel pour mettre à jour la liste affichée lorsque le recyclerView défile.

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

 }

Dans ce cas, après que le résultat de la requête de la méthode next a mis à jour le modèle de vue, le fragment n'est pas stimulé dans viewmodel.next.observer().

0voto

OPAH ! après un test unitaire, j'ai pu découvrir quel était le problème, le problème était ctrl + v, la propriété next, elle retournait la propriété _post et non la propriété _next, donc la vue n'était pas notifiée de la mise à jour. La correction s'est faite dans le viewmoldel, en changeant la méthode get de la variable next :

private val _next = MutableLiveData<Result<List<PostsDTO>>>()
val next: LiveData<Result<List<PostsDTO>>>
    get() =_next

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X