带有 viewmodel 和实时数据的 ViewPager,所有 6 个选项卡数据都被最后一个选项卡数据替换

ViewPager with viewmodel and live data , all 6 tabs data is replaced by last tab data

我正在研究一个 ViewPager 有 6 个 tabs 其中只有一个片段 TimesListFragment

根据传递给 TimesListFragment 的参数,它调用 api 例如;科学、技术、旅游等

我已经关注 Google 的 GithubBrowserSample 我的应用

我有 TimesListFragment -> TimesViewModel -> TimesRepository

有 6 个选项卡,当我点击 api 所有选项卡显示相同的结果,如果是最后一个 argument

StoriesPagerAdapter.kt

class StoriesPagerAdapter(fragmentManager: FragmentManager?)
    :FragmentStatePagerAdapter(fragmentManager){
    private val sections= arrayListOf("science","technology","business","world","movies","travel")
    override fun getItem(position: Int): Fragment {
        return TimesListFragment.newInstance(sections[position])
    }
    override fun getCount(): Int {
        return sections.size
    }
    override fun getPageTitle(position: Int): CharSequence? {
        return sections[position]
    }
}

问题:所有选项卡显示 travel 个参数的数据

TimesViewModel

class TimesViewModel @Inject constructor(private val timesRepository: TimesRepository) :
        ViewModel() {
    lateinit var data: LiveData<Resource<TimesStoriesResponse>>
    fun fetchStories(section:String): LiveData<Resource<TimesStoriesResponse>> {
        data = timesRepository.loadStories(section)
        return data
    }
}

TimesRepository.kt

class TimesRepository @Inject constructor(private val apiService: ApiService,
                                          private val timesDao: TimesDao,
                                          private val appExecutors: AppExecutors) {

    private val repoListRateLimit = RateLimiter<String>(10, TimeUnit.MINUTES)

    fun loadStories(section:String): LiveData<Resource<TimesStoriesResponse>> {
        return object : NetworkBoundResource<TimesStoriesResponse, TimesStoriesResponse>(appExecutors) {
            override fun saveCallResult(item: TimesStoriesResponse) {
                timesDao.insert(item)
            }

            override fun shouldFetch(data: TimesStoriesResponse?): Boolean {
                return data == null  || repoListRateLimit.shouldFetch(section)
            }

            override fun loadFromDb() = timesDao.load()

            override fun createCall() = apiService.getTopStories(section,ConfigConstant.TIMES_KEY)

            override fun onFetchFailed() {
                repoListRateLimit.reset(section)
            }
        }.asLiveData()
    }

ApiService.kt

interface ApiService {
    @GET("svc/topstories/v2/{section}.json?")
    fun getTopStories(@Path ("section") section:String,@Query("api-key") apiKey:String)
            :LiveData<ApiResponse<TimesStoriesResponse>>
}

TimesListFragment.kt

private fun initViewModel() {

        viewModel = ViewModelProviders.of(this, viewModelFactory).get(section,TimesViewModel::class.java)

    }

    private fun initData() {
        viewModel?.fetchStories(section)?.observe(this, Observer {

            when (it?.status) {

                Status.LOADING -> {
                    showLoading(true)
                    showError(false,null)
                }


                Status.SUCCESS -> {
                    showLoading(false)
                    showError(false,null)
                    showSuccessData(it.data)
                }

                Status.ERROR -> {
                    showLoading(false)
                    showError(true,it.message)
                }

            }
        })
    }

注意:这两个方法都是在 TimesListFragmnet

的 onViewCreated() 中调用的

由于您的 ViewModel,这应该会发生。

通常,由于 ViewModel 的设计工作方式,每个 ActivityFragment 有一个 ViewModel。使用 ViewModel 的主要好处之一是它的生命周期与 Fragment 的生命周期完全分开,因此,您的 Fragment 可以多次销毁和重新创建,并且您将仍然能够恢复存储在 ViewModel 中的当前数据。

因此,这意味着使用从 ViewModelProviders 获取 ViewModel 的典型代码,您将获取完全相同的 ViewModel

通常,这不会造成问题,但在您的 ViewPager 中,您正在重复使用相同的 TimesListFragment,这 最有可能 调用相同的 ViewModel,因此导致每个片段显示相同的数据。

解决方法是使用:

ViewModelProviders.of(this).get(KEY, TimesViewModel::class.java)

注意用于区分需要获取的 ViewModel 的 KEY。因此,通过使用 ViewPager 中的位置作为唯一键,您应该能够为每个 TimesListFragment.

拥有唯一的 ViewModel

您需要告诉 ViewPager 不要加载当前未显示的页面。默认情况下,我认为它至少会向左加载一页,向右加载一页。