简体   繁体   中英

Using Android Paging Library to display filtered results from same 2 loaded fragments in ViewPager with FragmentPagerAdapter

I have a single activity app which is centered around a fragment that will list data from online API. I use the same fragment throughout the app filtering the data as needed.

Filtering is done by having parameters passed through the Factory to the DataSource. All this works fine if I only have one fragment loaded. I can filter and search just fine.

Problems start when trying to load multiple fragments in a ViewPager. For example Fragment A - is filtered to show active items Fragment B - is filtered to show expired items both are to be displayed in a ViewPager using FragmentPagerAdapter. When I run the app both Fragments get filted for expired items (the last fragment added to viewpager) It seems the parameters that Fragment A set in the DataSource are overwritten immediately by Fragment B. Fragment A loadInitial works as it should but loadAfter is using the parameters from Fragment B.

should also note if I invalidate() the DataSource it returns the correct filtered data without issue. (so its just when loaded my params get overwritten and loadAfter uses incorrect param)

Tried messing with the Observer using getViewLifecycleOwner() trying to figure if they way I am calling the DataSource is the issue, Tried the various ViewPager Adapters FragmentStatePagerAdapter and FragmentPagerAdapter.

All the checks show the DataSource getting the correct parameter then show it getting overwritten almost immediately.

So question is the call to PageKeyedDataSource not unique to the Fragment Calling it? Why are my variables being overwritten by another fragment.

Can anyone say where I should be looking to resolve this issue.

Side note I have three fragments loaded using FragmentStatePagerAdapter with 2 of 3 being the Same Fragment Called with different params. It works fine because with three fragments loaded the FragmentStatePagerAdapter apparently doesnt initialize the 3rd fragment until after everything is loaded so it does not trip over the other fragment.

I was facing with same problem view pager loads two fragment a next fragment override the value. So here's how i did it.

In fragment i set list to view model.

companion object {
private const val ARG_SECOND = "arg_second"

@JvmStatic
fun newInstance(second: Array<Pair<String, String>>): PlaceholderFragment {
    return PlaceholderFragment().apply {
        arguments = Bundle().apply {
            putString(ARG_SECOND,second[0].second)
        }
    }
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProviders.of(this).get(PageViewModel::class.java).apply {
    setListId(arguments?.getString(ARG_SECOND) ?: "")
}
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    btn.setOnClickListener {
        viewModel.setListId("my id")
        viewModel.videosList.value?.dataSource?.invalidate()       
    }
}

In view model i create a method setListId:

class PageViewModel(application: Application) : AndroidViewModel(application) 
{

//paging
private val networkService = NetworkService.getService()
var videosList: LiveData<PagedList<Item>>
private val compositeDisposable = CompositeDisposable()
private val pageSize = 50
private val videosDataSourceFactory: VideosDataSourceFactory

init {
videosDataSourceFactory = VideosDataSourceFactory(compositeDisposable, 
networkService)
    val config = PagedList.Config.Builder()
        .setPageSize(pageSize)
        .setInitialLoadSizeHint(pageSize)
        .setEnablePlaceholders(false)
        .build()
    videosList = LivePagedListBuilder<String, Item>(videosDataSourceFactory, 
config).build()
}

fun getState(): LiveData<State> = Transformations.switchMap<VideosDataSource,
        State> 
(videosDataSourceFactory.videosDataSourceLiveData,VideosDataSource::state)

fun retry() {
    videosDataSourceFactory.videosDataSourceLiveData.value?.retry()
}
fun setListId(listId: String){
    videosDataSourceFactory.listId = listId
}


override fun onCleared() {
    super.onCleared()
    compositeDisposable.dispose()
}

fun listIsEmpty(): Boolean {
    return videosList.value?.isEmpty() ?: true
}
}

In data source factory i craete a variable:

val videosDataSourceLiveData = MutableLiveData<VideosDataSource>()
lateinit var listId:String

override fun create(): DataSource<String, Item> {
val videosDataSource = VideosDataSource(networkService, 
compositeDisposable,listId)
videosDataSourceLiveData.postValue(videosDataSource)
return videosDataSource
}

And then get it via constructor of data source here:

class VideosDataSource(
private val networkService: NetworkService,
private val compositeDisposable: CompositeDisposable,
private val listId: String
): PageKeyedDataSource<String, Item>() {
var state: MutableLiveData<State> = MutableLiveData()
private var retryCompletable: Completable? = null

override fun loadInitial(params: LoadInitialParams<String>, callback: 
LoadInitialCallback<String, Item>) {
updateState(State.LOADING)
compositeDisposable.add(
        networkService.getPlaylistVideos(listId
        ,""
        ,Constants.API_KEY)
        .subscribe( { response ->
            updateState(State.DONE)
            callback.onResult(response.items, response.prevPageToken, 
response.nextPageToken)
        },
                {
                    updateState(State.ERROR)
                    setRetry(Action { loadInitial(params,callback) })
                }
        )
)
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM