gpt4 book ai didi

android - Paging库在向上滑动刷新后只调用LoadInitial,不调用LoadAfter

转载 作者:太空宇宙 更新时间:2023-11-03 10:33:45 24 4
gpt4 key购买 nike

我正在尝试使用 Paging 库 + RxJava 创建应用程序,一切正常,它从后端加载数据并将其插入我的 Recyclerview,当我向下滚动 LoadAfter 正常加载下一页时,我的问题是当我向上滑动时使用 SwipeLayoutRefrech 刷新数据会使数据源无效,它只调用 LoadInitial(仅加载 20 个第一项),而 LoadAfter 根本不调用,我花了很多时间但仍然没有解决方案。

这是我的数据源工厂:

public class GalleryPhotosDataSourceFactory extends DataSource.Factory<Integer, GalleryPhotosItems> {

private CompositeDisposable compositeDisposable;

private MutableLiveData<GalleryPhotosDataSource> DataSourceMutableLiveData = new MutableLiveData<>();


/**
* Create a DataSource.
* <p>
* The DataSource should invalidate itself if the snapshot is no longer valid. If a
* DataSource becomes invalid, the only way to query more data is to create a new DataSource
* from the Factory.
* <p>
* {@link } for example will construct a new PagedList and DataSource
* when the current DataSource is invalidated, and pass the new PagedList through the
* {@code LiveData<PagedList>} to observers.
*
* @return the new DataSource.
*/

public GalleryPhotosDataSourceFactory(CompositeDisposable compositeDisposable) {
this.compositeDisposable = compositeDisposable;
}

@Override
public DataSource<Integer, GalleryPhotosItems> create() {
GalleryPhotosDataSource galleryPhotosDataSource = new GalleryPhotosDataSource(compositeDisposable);
DataSourceMutableLiveData.postValue(galleryPhotosDataSource);
return galleryPhotosDataSource;
}

@NonNull
public MutableLiveData<GalleryPhotosDataSource> getDataSourceLiveData() {
return DataSourceMutableLiveData ;
}
}

我的类数据源用于从我的服务器加载数据:

public class GalleryPhotosDataSource extends ItemKeyedDataSource<Integer, GalleryPhotosItems> {

public static final String TAG = "GalleryPhotosDataSource";

private ApiService apiService;

private CompositeDisposable compositeDisposable;

private MutableLiveData<NetworkState> networkState = new MutableLiveData<>();

private MutableLiveData<NetworkState> initialLoad = new MutableLiveData<>();

/**
* Keep Completable reference for the retry event
*/
private Completable retryCompletable;

GalleryPhotosDataSource(CompositeDisposable compositeDisposable) {
this.apiService = Client.createAppService();
this.compositeDisposable = compositeDisposable;
}

public void retry() {
if (retryCompletable != null) {
compositeDisposable.add(retryCompletable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(() -> {
}, throwable -> Timber.e(throwable.getMessage())));
}
}




/**
* Load initial data.
* <p>
* This method is called first to initialize a PagedList with data. If it's possible to count
* the items that can be loaded by the DataSource, it's recommended to pass the loaded data to
* the callback via the three-parameter
* {@link LoadInitialCallback#onResult(List, int, int)}. This enables PagedLists
* presenting data from this source to display placeholders to represent unloaded items.
* <p>
* {@link LoadInitialParams#requestedInitialKey} and {@link LoadInitialParams#requestedLoadSize}
* are hints, not requirements, so they may be altered or ignored. Note that ignoring the
* {@code requestedInitialKey} can prevent subsequent PagedList/DataSource pairs from
* initializing at the same location. If your data source never invalidates (for example,
* loading from the network without the network ever signalling that old data must be reloaded),
* it's fine to ignore the {@code initialLoadKey} and always start from the beginning of the
* data set.
*
* @param params Parameters for initial load, including initial key and requested size.
* @param callback Callback that receives initial load data.
*/
@Override
public void loadInitial(@NonNull LoadInitialParams<Integer> params, @NonNull LoadInitialCallback<GalleryPhotosItems> callback) {
// update network states.
// we also provide an initial load state to the listeners so that the UI can know when the
// very first list is loaded.
networkState.postValue(NetworkState.LOADING);
initialLoad.postValue(NetworkState.LOADING);

//get the initial users from the api
compositeDisposable.add(apiService.getPhotosGallery(1, 5, -1, params.requestedLoadSize).subscribe(galleryPhotosItemses -> {
// clear retry since last request succeeded
setRetry(null);
networkState.postValue(NetworkState.LOADED);
initialLoad.postValue(NetworkState.LOADED);
callback.onResult(galleryPhotosItemses);
},
throwable -> {
// keep a Completable for future retry
setRetry(() -> loadInitial(params, callback));
NetworkState error = NetworkState.error(throwable.getMessage());
// publish the error
networkState.postValue(error);
initialLoad.postValue(error);
}));
}

/**
* Load list data after the key specified in {@link LoadParams#key LoadParams.key}.
* <p>
* It's valid to return a different list size than the page size if it's easier, e.g. if your
* backend defines page sizes. It is generally safer to increase the number loaded than reduce.
* <p>
* Data may be passed synchronously during the loadAfter method, or deferred and called at a
* later time. Further loads going down will be blocked until the callback is called.
* <p>
* If data cannot be loaded (for example, if the request is invalid, or the data would be stale
* and inconsistent, it is valid to call {@link #invalidate()} to invalidate the data source,
* and prevent further loading.
*
* @param params Parameters for the load, including the key to load after, and requested size.
* @param callback Callback that receives loaded data.
*/
@Override
public void loadAfter(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<GalleryPhotosItems> callback) {
// set network value to loading.
networkState.postValue(NetworkState.LOADING);

//get the users from the api after id
compositeDisposable.add(apiService.getPhotosGallery(1, 5, params.key-1, params.requestedLoadSize).subscribe(galleryPhotosItemses -> {
// clear retry since last request succeeded
setRetry(null);
networkState.postValue(NetworkState.LOADED);
callback.onResult(galleryPhotosItemses);
},
throwable -> {
// keep a Completable for future retry photos_user/5/1534161047_1534160953579.jpg
setRetry(() -> loadAfter(params, callback));
// publish the error
networkState.postValue(NetworkState.error(throwable.getMessage()));
}));

}

/**
* Load list data before the key specified in {@link LoadParams#key LoadParams.key}.
* <p>
* It's valid to return a different list size than the page size if it's easier, e.g. if your
* backend defines page sizes. It is generally safer to increase the number loaded than reduce.
* <p>
* <p class="note"><strong>Note:</strong> Data returned will be prepended just before the key
* passed, so if you vary size, ensure that the last item is adjacent to the passed key.
* <p>
* Data may be passed synchronously during the loadBefore method, or deferred and called at a
* later time. Further loads going up will be blocked until the callback is called.
* <p>
* If data cannot be loaded (for example, if the request is invalid, or the data would be stale
* and inconsistent, it is valid to call {@link #invalidate()} to invalidate the data source,
* and prevent further loading.
*
* @param params Parameters for the load, including the key to load before, and requested size.
* @param callback Callback that receives loaded data.
*/
@Override
public void loadBefore(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<GalleryPhotosItems> callback) {

}

/**
* Return a key associated with the given item.
* <p>
* If your ItemKeyedDataSource is loading from a source that is sorted and loaded by a unique
* integer ID, you would return {@code item.getID()} here. This key can then be passed to
* {@link #loadBefore(LoadParams, LoadCallback)} or
* {@link #loadAfter(LoadParams, LoadCallback)} to load additional items adjacent to the item
* passed to this function.
* <p>
* If your key is more complex, such as when you're sorting by name, then resolving collisions
* with integer ID, you'll need to return both. In such a case you would use a wrapper class,
* such as {@code Pair<String, Integer>} or, in Kotlin,
* {@code data class Key(val name: String, val id: Int)}
*
* @param item Item to get the key from.
* @return Key associated with given item.
*/
@NonNull
@Override
public Integer getKey(@NonNull GalleryPhotosItems item) {
return item.getPhoto_id();
}

@NonNull
public MutableLiveData<NetworkState> getNetworkState() {
return networkState;
}

@NonNull
public MutableLiveData<NetworkState> getInitialLoad() {
return initialLoad;
}

private void setRetry(final Action action) {
if (action == null) {
this.retryCompletable = null;
} else {
this.retryCompletable = Completable.fromAction(action);
}
}
}

我的 View 模型:

public class GalleryPhotosViewModel extends ViewModel {

public LiveData<PagedList<GalleryPhotosItems>> photosList;

private CompositeDisposable compositeDisposable = new CompositeDisposable();

private static final int pageSize = 10;

private GalleryPhotosDataSourceFactory photosDataSourceFactory;

public GalleryPhotosViewModel() {
photosDataSourceFactory = new GalleryPhotosDataSourceFactory(compositeDisposable);

PagedList.Config config = new PagedList.Config.Builder()
.setPageSize(pageSize)
.setInitialLoadSizeHint(pageSize * 2)
.setEnablePlaceholders(true)
.build();

photosList = new LivePagedListBuilder<>(photosDataSourceFactory, config).build();


}

public void retry() {
photosDataSourceFactory.getDataSourceLiveData().getValue().retry();
}

public void refresh() {
photosDataSourceFactory.getDataSourceLiveData().getValue().invalidate();
}

public LiveData<NetworkState> getNetworkState() {
return Transformations.switchMap(photosDataSourceFactory.getDataSourceLiveData(), GalleryPhotosDataSource::getNetworkState);
}

public LiveData<NetworkState> getRefreshState() {
return Transformations.switchMap(photosDataSourceFactory.getDataSourceLiveData(), GalleryPhotosDataSource::getInitialLoad);
}

@Override
protected void onCleared() {
super.onCleared();
compositeDisposable.dispose();
}
}

最后是我的 initAdapter 和 SwipeRefreshLayout fragment :

public class AbonnesFragment extends Fragment  implements RetryCallback{

@BindView(usersSwipeRefreshLayout)
SwipeRefreshLayout mSwipeRefreshLayout;

@BindView(R.id.usersRecyclerView)
RecyclerView usersRecyclerView;

@BindView(R.id.errorMessageTextView)
TextView errorMessageTextView;

@BindView(R.id.retryLoadingButton)
Button retryLoadingButton;

@BindView(R.id.loadingProgressBar)
ProgressBar loadingProgressBar;

private GalleryPhotosViewModel viewModel;

private GalleryPhotosAdapter Adapter;

View rootView;

public AbonnesFragment() {
// Required empty public constructor
}


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_photos, container, false);

ButterKnife.bind(this, rootView);
viewModel = ViewModelProviders.of(this).get(GalleryPhotosViewModel.class);
initAdapter();
initSwipeToRefresh();

return rootView;
}



private void initAdapter() {
GridLayoutManager gridLayoutManager = new GridLayoutManager(getActivity(), 3);
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
switch(Adapter.getItemViewType(position)){
case R.layout.gallery_photos_items:
return 1;
case R.layout.item_network_state:
return 3;
default:
return -1;
}
}
});

Adapter = new GalleryPhotosAdapter(this);
usersRecyclerView.setLayoutManager(gridLayoutManager);
usersRecyclerView.setAdapter(Adapter);

viewModel.photosList.observe(this, Adapter::submitList);
viewModel.getNetworkState().observe(this, Adapter::setNetworkState);
}

/**
* Init swipe to refresh and enable pull to refresh only when there are items in the adapter
*/
private void initSwipeToRefresh() {
viewModel.getRefreshState().observe(this, networkState -> {
if (networkState != null) {
if (Adapter.getCurrentList() != null) {
if (Adapter.getCurrentList().size() > 0) {
mSwipeRefreshLayout.setRefreshing(networkState.getStatus() == NetworkState.LOADING.getStatus());
} else {
setInitialLoadingState(networkState);
}
} else {
setInitialLoadingState(networkState);
}
}
});
mSwipeRefreshLayout.setOnRefreshListener(() -> viewModel.refresh());

}

/**
* Show the current network state for the first load when the user list
* in the adapter is empty and disable swipe to scroll at the first loading
*
* @param networkState the new network state
*/
private void setInitialLoadingState(NetworkState networkState) {
//error message
errorMessageTextView.setVisibility(networkState.getMessage() != null ? View.VISIBLE : View.GONE);
if (networkState.getMessage() != null) {
errorMessageTextView.setText(networkState.getMessage());
}

//loading and retry
retryLoadingButton.setVisibility(networkState.getStatus() == Status.FAILED ? View.VISIBLE : View.GONE);
loadingProgressBar.setVisibility(networkState.getStatus() == Status.RUNNING ? View.VISIBLE : View.GONE);
if (networkState.getStatus() == Status.SUCCESS){
mSwipeRefreshLayout.setEnabled(true);
} else {
mSwipeRefreshLayout.setEnabled(false);

}
}

@OnClick(R.id.retryLoadingButton)
void retryInitialLoading() {
viewModel.retry();

}

@Override
public void retry() {
viewModel.retry();

}
}

我希望能找到可以帮助我的人,谢谢

最佳答案

这似乎是一个已报告并已修复的错误 https://issuetracker.google.com/issues/113122599

已在 2.1.0-alpha01 中修复

Fixed a case where an extremely small initial load size together with unchanged data would result in no further loading b/113122599

https://developer.android.com/jetpack/docs/release-notes

关于android - Paging库在向上滑动刷新后只调用LoadInitial,不调用LoadAfter,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52244969/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com