How to maintain same DataSource for List,Filter and Search in Android Paging Library
Asked Answered
C

1

2

I am having activity that displays list of items and also having filter and search option. I am displaying the items using android paging library. First time the List of items will be loaded its working fine when I am Scrolling to bottom next set of items getting loaded. But I also want to filter the items and Search the items. On Filtering or Searching item I am invalidating the existing source.if am not invalidate the Data Source the filter and Search api is not firing.I want to load list of new items based on my filter and Search key using Data Source.

executor = Executors.newFixedThreadPool(5);
    celebrityDataFactory = new CelebrityDataFactory(apicallInterface,         mFansismParam);
    networkState =  Transformations.switchMap(celebrityDataFactory.getCelebrityData(),
            dataSource -> dataSource.getNetworkState());

    PagedList.Config pagedListConfig =
            (new PagedList.Config.Builder())
                    .setEnablePlaceholders(false)
                    .setPrefetchDistance(8)
                    .setInitialLoadSizeHint(10)
                    .setPageSize(20).build();
    if (!mFansismParam.getCategoryId().isEmpty()) {
        celebrityDetails = new LivePagedListBuilder(celebrityDataFactory, pagedListConfig)
                .setFetchExecutor(executor)
                .build();
    } else(!mFansismParam.getProfessionId().isEmpty()) {
        celebrityDetails = new LivePagedListBuilder(celebrityDataFactory, pagedListConfig)
                .setFetchExecutor(executor)
                .build();
    }

Data Factory to create Data Source

@Override
public DataSource create() {
    celebrityDataSource = new CelebrityDataSource(apicallInterface,   params);
    celebrityData.postValue(celebrityDataSource);
    return celebrityDataSource;
}

Retrofit API Call:

 Call<CelebrityList> getCelebrityList(@Query("categoryId") String categoryId,
                                     @Query("professionId") String professionId,
                                     @Query("page") String pageNumber,
                                     @Query("name") String searchKey);

Data Source Api CallBack:

apicallInterface.getCelebrityList(requestParams.getCategoryId(), "", "1", "").enqueue(new Callback<CelebrityList>() {
        @Override
        public void onResponse(Call<CelebrityList> call, Response<CelebrityList> response) {
            if (response.isSuccessful()) {
                initialLoading.postValue(NetworkState.LOADED);
                networkState.postValue(NetworkState.LOADED);
                if (!response.body().getData().isEmpty()) {
                    callback.onResult(response.body().getData(), null, "2");
                } else {
                    networkState.postValue(new NetworkState(NetworkState.Status.SUCCESS, "No more results"));
                }
            } else {
                initialLoading.postValue(new NetworkState(NetworkState.Status.FAILED, response.message()));
                networkState.postValue(new NetworkState(NetworkState.Status.FAILED, response.message()));
            }
        }
Cyanite answered 8/9, 2019 at 10:17 Comment(0)
M
5

You need to hold your search key in live data so that pagedlist can be changed whenever it changes. So in your viewmodel, define:

public MutableLiveData<String> filterTextAll = new MutableLiveData<>();

Since the pagedlist is defined as a LiveData too, this can be done with the help of Transformation. Transformations class provide you with functions with which you can change the value in your LiveData object. swithMap function returns a new LiveData object rather than a value, in your case, searchkey is switched to get the pagedList object corresponding to the searchkey by create new datasource under the hood.

pagedListLiveData = Transformations.switchMap(filterTextAll, input -> {
        MyDataSourceFactory myDataSourceFactory = new MyDataSourceFactory(executor,input);
        myDataSource = myDataSourceFactory.getMyDataSourceMutableLiveData();
        networkState = Transformations.switchMap(myDataSource,
        dataSource -> dataSource.getNetworkState());
        return (new LivePagedListBuilder(myDataSourceFactory, pagedListConfig))
          .setFetchExecutor(executor)
          .build();
      });

You can to change your DataSourceFactory and DataSource constructor to add searchKey param:

public class MyDataSourceFactory extends DataSource.Factory {

  MutableLiveData<MyDataSource> myDataSourceMutableLiveData;
  private MyDataSource myDataSource;
  private Executor executor;
  private String searchKey;

  public MyDataSourceFactory(Executor executor , String searchKey) {
    this.executor= executor;
    this.searchKey= searchKey;
    this.myDataSourceMutableLiveData= new MutableLiveData<>();
  }

  @Override
  public DataSource create() {
    //*notice: It's important that everytime a DataSource factory create() is invoked a new DataSource instance is created
    myDataSource= new MyDataSource(executor, searchKey);
    myDataSourceMutableLiveData.postValue(myDataSource);
    return myDataSource;
  }

  public MutableLiveData<MyDataSource> getMyDataSourceMutableLiveData() {
    return myDataSourceMutableLiveData;
  }

  public MyDataSource getMyDataSource() {
    return myDataSource;
  }

}

do same like above for DataSource constructor to pass searchKey to use in api call. And one thing remains, in your Activity/Fragment (lifeCycleOwner) set value of filterTextAll mutableLiveData whenever searchkey change fired, like trigger searchview onQueryTextChange or any event you like.

private void performSearch(String searchKey) {
        // TODO: Perform the search and update the UI to display the results.
            myViewModel.filterTextAll.setValue(searchKey);
            myViewModel.pagedListLiveData.observe(owner, new Observer<PagedList<MyItem>>() {
            @Override
            public void onChanged(PagedList<MyItem> myItems) {
              myAdapter.submitList(myItems);
            }
          }); 
            myViewModel.networkState.observe(owner, new Observer<NetworkState>() {
               @Override
               public void onChanged(NetworkState networkState) {
                  myAdapter.setNetworkState(networkState);
               }
           });

            myRecyclerView.setAdapter(myAdapter);
          }
Martel answered 9/9, 2019 at 9:28 Comment(3)
its working perfectly. I understood that we have to use input param also as Live Data. So that whenever the input get changed. Transformations.Switchmap() trigger to pull the new data Source.Cyanite
You're right. In other words, each time the searchKey is changed (When the filterTextAll.setValue(searchKey) method is called) the switchMap() transformation is called and pagedListLiveData instance is updated with new value based upon the searchKey.Martel
How can I achieve that if I'm using dagger to inject DataSourceFactory and DataSource? Am I stuck with setter methods to propagate the searchKey between DataSourceFactory and DataSource?Gman

© 2022 - 2024 — McMap. All rights reserved.