【问题标题】:LiveData is not updating the View consistentlyLiveData 没有一致地更新视图
【发布时间】:2019-07-05 11:40:06
【问题描述】:

recycleView 不会在初始加载时从网络更新结果。

回收视图:


    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        mRecyclerAdapter = MovieListAdapter(context)

        rvMovieList.apply {
            // Dedicated layouts for Screen Orientation
            if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
                layoutManager = LinearLayoutManager(context)
            } else {
                layoutManager = GridLayoutManager(context, 2)
            }

            adapter = mRecyclerAdapter
        }
    }

并使用 ViewModel 中的 LiveData 监听网络结果。

LiveData监听sn-p下面的Fragment:

    override fun onResume() {
        super.onResume()

        // Listen to data change
        viewModel.getMovies().observe(this, mMovieListObserver)
    }

    private val mMovieListObserver: Observer<PagedList<MovieItem>> = Observer { movieItems ->
        Log.d(TAG, "MovieItems: ${movieItems.size}")
        showEmptyList(movieItems?.size == 0)
        mRecyclerAdapter.submitList(movieItems)
    }

    private fun showEmptyList(isEmpty: Boolean) {
        tvEmptyListView.visibility = if (isEmpty) View.VISIBLE else View.GONE
        rvMovieList.visibility = if (isEmpty) View.GONE else View.VISIBLE
    }

    override fun onPause() {
        viewModel.getMovies().removeObserver(mMovieListObserver)

        super.onPause()
    }

具有讽刺意味的是,结果会在后续加载时填充 recycleView。我觉得 LiveData 没有按预期工作。引入emptyView时的期望是根据网络结果显示/隐藏recycleView/EmptyView。

ViewModel 粘贴在下面:


class MovieListViewModel : ViewModel() {

    private val PAGE_SIZE = 10

     internal var movies: LiveData<PagedList<MovieItem>>

    init {
        val dataSourceFactory = MovieDataSourceFactory()

        val pagedListConfig = PagedList.Config.Builder()
                .setInitialLoadSizeHint(PAGE_SIZE)
                .setPageSize(PAGE_SIZE)
                .setEnablePlaceholders(true)
                .build()

        movies = LivePagedListBuilder(dataSourceFactory, pagedListConfig)
                // .setBoundaryCallback() TODO
                .build()
    }

    fun getMovies(): LiveData<PagedList<MovieItem>> {
        return movies
    }
}

感谢您抽出宝贵时间,感谢您对解决方案或最佳做法的任何意见。谢谢。

回购:https://gitlab.com/faisalm/MovieDirect

////---

更新了 DataSourceFactory 和 DataSource。

class MovieDataSourceFactory : DataSource.Factory<Int, MovieItem>() {

    private val mutableLiveData = MutableLiveData<MovieDataSource>()

    override fun create(): DataSource<Int, MovieItem> {
        val dataSource = MovieDataSource()
        mutableLiveData.postValue(dataSource)
        return dataSource
    }
}


class MovieDataSource internal constructor() : PageKeyedDataSource<Int, MovieItem>() {

    private val movieDbService: MovieDbService = RetrofitFactory.create()

    override fun loadInitial(params: LoadInitialParams<Int>, callback: LoadInitialCallback<Int, MovieItem>) {

        val moviesListCall = movieDbService.fetchLatestMoviesPaged(Constants.API_KEY, 1)
        moviesListCall.enqueue(object : Callback<MoviesList> {
            override fun onResponse(call: Call<MoviesList>, response: Response<MoviesList>) {

                if (response.isSuccessful) {
                    val moviesLists = response.body()?.results
                    callback.onResult(moviesLists!!, 1, 2)
                }
            }

            override fun onFailure(call: Call<MoviesList>, t: Throwable) {}
        })
    }

    override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Int, MovieItem>) {}

    override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, MovieItem>) {

        val moviesListCall = movieDbService.fetchLatestMoviesPaged(Constants.API_KEY, params.key)

        moviesListCall.enqueue(object : Callback<MoviesList> {
            override fun onResponse(call: Call<MoviesList>, response: Response<MoviesList>) {

                if (response.isSuccessful) {
                    val moviesLists = response.body()?.results
                    callback.onResult(moviesLists!!, params.key + 1)
                }
            }

            override fun onFailure(call: Call<MoviesList>, t: Throwable) {}
        })
    }
}

【问题讨论】:

  • “结果在后续加载时填充recycleView”是什么意思?
  • 您是否通知适配器mRecyclerAdapter.submitList(movieItems) 数据更改? (类似notifyDataSetChanged 或更好的东西)
  • @JeelVankhede 不需要那个,因为他正在使用 PagedList
  • 您是否遇到任何错误,如果您发布您的 DataSource 类可能会很好?
  • @RahulShukla 虽然结果没有反映在初始加载中,但一旦用户回家/离开应用程序并回来,就会填充列表。

标签: android kotlin android-recyclerview viewmodel android-livedata


【解决方案1】:

我认为问题在于您为 liveData 添加和删除观察者的方式。

不要在onResume 中添加和在onPause 中删除,只需在片段中的onActivityCreated 中观察它。 LiveData 的观察方法接受 LifeCycleOwner(这是您在 Fragment 中使用 this 传递的内容),它会确保它在该生命周期中的正确时间进行观察。

所以删除这些行:

viewModel.getMovies().removeObserver(mMovieListObserver)viewModel.getMovies().addObserver(this, mMovieListObserver)

并添加:

override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)
    viewModel.getMovies().observe(this, Observer { movieItems ->
        Log.d(TAG, "MovieItems: ${movieItems.size}")
        showEmptyList(movieItems?.loadedCount == 0)
        mRecyclerAdapter.submitList(movieItems)
    })
}

【讨论】:

  • 感谢您的意见。我之前尝试过这个选项,但没有奏效。发现movieItems.size 是顽固的,尽管观察者一旦从远程接收到更新值就会发出更新的值。评论 showEmptyList() 结果符合预期,并更新了值。强烈觉得空列表处理应该重新审视。
  • 这可能与使用 movieItems?.sizemovieItems?.loadedCount 有关(我相信大小是加载计数 + 占位符计数),但看起来实现空视图的正确方法是使用LivePagedListBuilder.setBoundaryCallback?可能是在内部,如果视图被隐藏,那么它被认为是“不可观察的”。另一个解决方法是让rvMovieList 保持可见,仅更改tvEmptyListView 的可见性
  • 在这种情况下,showEmptyList 应该如下所示: private fun showEmptyList(isEmpty: Boolean) { tvEmptyListView.visibility = if (isEmpty) View.VISIBLE else View.GONE }
猜你喜欢
  • 2019-05-16
  • 1970-01-01
  • 2015-03-27
  • 2019-05-01
  • 2015-01-01
  • 2022-01-11
  • 2018-02-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多