recyclerView.addOnScrollListener - "retrofit pagination with MVVM" 正在加载相同的 response/list
recyclerView.addOnScrollListener - "retrofit pagination with MVVM" is loading the same response/list
我在我的应用程序中使用 blogger API、retrofit 和 MVVM,我试图在用户滚动时使用分页来加载更多帖子,这里发生的问题是响应正在自行加载“相同列表/相同的十个帖子再次加载
这是我的代码
PostsClient Class
public class PostsClient {
private static final String TAG = "PostsClient";
private static final String KEY = "XYZ sensitive key!";
private static final String BASE_URL = "https://www.googleapis.com/blogger/v3/blogs/4294497614198718393/";
private PostInterface postInterface;
private static PostsClient INSTANCE;
public PostsClient() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
postInterface = retrofit.create(PostInterface.class);
}
public static PostsClient getINSTANCE() {
if(INSTANCE == null){
INSTANCE = new PostsClient();
}
return INSTANCE;
}
public Call<PostList> getPostList(){
return postInterface.getPostList(KEY);
}
}
[PostViewModel]
public class PostViewModel extends ViewModel {
public static final String TAG = "PostViewModel";
public MutableLiveData<PostList> postListMutableLiveData = new MutableLiveData<>();
public MutableLiveData<PostList> postListByLabelMutableLiveData = new MutableLiveData<>();
public MutableLiveData<String> finalURL = new MutableLiveData<>();
public MutableLiveData<String> token = new MutableLiveData<>();
public void getPosts(){
if (token.getValue() != "") {
finalURL.setValue(finalURL.getValue() + "&pageToken=" + token.getValue());
}
if (token == null) {
return;
}
PostsClient.getINSTANCE().getPostList().enqueue(new Callback<PostList>() {
@Override
public void onResponse(@NotNull Call<PostList> call, @NotNull Response<PostList> response) {
PostList list = response.body();
if (list.getItems() != null) {
token.setValue(list.getNextPageToken());
postListMutableLiveData.setValue(list);
}
Log.i(TAG,response.body().getItems().toString());
}
@Override
public void onFailure(Call<PostList> call, Throwable t) {
Log.e(TAG,t.getMessage());
}
});
}
public void getPostListByLabel(){
PostsByLabelClient.getINSTANCE().getPostListByLabel(finalURL.getValue()).enqueue(new Callback<PostList>() {
@Override
public void onResponse(Call<PostList> call, Response<PostList> response) {
postListByLabelMutableLiveData.setValue(response.body());
}
@Override
public void onFailure(Call<PostList> call, Throwable t) {
}
});
}
}
HomeFragmentClass“主页”
public class HomeFragment extends Fragment {
private PostViewModel postViewModel;
public static final String TAG = "HomeFragment";
private RecyclerView recyclerView;
private PostAdapter postAdapter;
private List<Item> itemArrayList;
private boolean isScrolling = false;
private int currentItems, totalItems, scrollOutItems, selectedIndex;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
postViewModel = new ViewModelProvider(this).get(PostViewModel.class);
postViewModel.getPosts();
View root = inflater.inflate(R.layout.fragment_home, container, false);
itemArrayList = new ArrayList<>();
recyclerView = root.findViewById(R.id.homeRecyclerView);
postAdapter = new PostAdapter(getContext(),itemArrayList);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(linearLayoutManager);
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext()
, linearLayoutManager.getOrientation());
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.addItemDecoration(dividerItemDecoration);
recyclerView.setAdapter(postAdapter);
// textView.setText(s);
postViewModel.postListMutableLiveData.observe(HomeFragment.this, new Observer<PostList>() {
@Override
public void onChanged(PostList postList) {
itemArrayList.addAll(postList.getItems());
postAdapter.notifyDataSetChanged();
}
});
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
isScrolling = true;
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy > 0) {
currentItems = linearLayoutManager.getChildCount();
totalItems = linearLayoutManager.getItemCount();
scrollOutItems = linearLayoutManager.findFirstVisibleItemPosition();
if (isScrolling && (currentItems + scrollOutItems == totalItems)) {
isScrolling = false;
postViewModel.getPosts();
postAdapter.notifyDataSetChanged();
}
}
}
});
return root;
}
}
'更多解释
在 PostViewModel 上
我创建了一个变量
public MutableLiveData<String> token = new MutableLiveData<>();
这个代表新 page/response 的令牌将带有“每个页面都有一个列表/十个新帖子”
在 HomeFragment 上
我创建了三个整数值
private int currentItems, totalItems, scrollOutItems, selectedIndex;
和一个布尔值
private boolean isScrolling = false;
然后我用了recyclerView.addOnScrollListener
用这种方式加载接下来的十个帖子,但它不像我之前说的那样工作,加载相同 result/list
The result on imgur.com
每次您想获取新帖子时,您都会更改 finalURL
值:
if (token.getValue() != "") {
finalURL.setValue(finalURL.getValue() + "&pageToken=" + token.getValue());
}
这里你使用 finalURL.getValue()
,它已经包含旧的令牌值(上一页的)。第一页就可以了。
当您返回下一页时,您将获得 finalURL
的值并将新标记连接到它,尽管当前 finalURL
已经包含最后一个标记的值。所以现在 finalURL
包含几个标记,我认为 API 可以采用第一个标记,即上一页的标记(因此,您将获得相同的帖子列表)。
因此您需要将其更改为 baseURL 的常量值:
final String baseURL = "" // add base URL which is basically the initial value of the `finalURL`
if (token.getValue() != "") {
finalURL.setValue(baseURL + "&pageToken=" + token.getValue());
}
旁注:
如果您打算将字符串与 token.getValue() != ""
进行比较,则需要将其更改为字符串 .equals()
或使用 isEmpty()
方法检查空字符串。
考虑使用新的分页库。这里有一个sample and the documentation。它的性能更好,您不必手动跟踪滚动的位置。通过遵循示例,我发现实现它相当容易,尽管它有很多样板代码。## Heading ##
经过数百次尝试,终于解决了,这里是问题的解决方案
首先 我更改了 API PostInterface
中的 GET
方法并使其采用 @URL
而不是 @Query
KEY 像这样
public interface PostInterface {
@GET
Call<PostList> getPostList(@Url String URL);
}
Secondary 我编辑了 PostsClient
从 BASE_URL
private static String BASE_URL
中删除的 final 并创建了 setter & getter 对于 BASE URL & KEY
public static String getKEY() {
return KEY;
}
public static String getBaseUrl() {
return BASE_URL;
}
第三次也是最后一次 我在响应后为令牌检查程序移动了这个 if 语句
public void getPosts(){
Log.e(TAG,finalURL.getValue());
PostsClient.getINSTANCE().getPostList(finalURL.getValue()).enqueue(new Callback<PostList>() {
@Override
public void onResponse(@NotNull Call<PostList> call, @NotNull Response<PostList> response) {
PostList list = response.body();
if (list.getItems() != null) {
Log.e(TAG,list.getNextPageToken());
token.setValue(list.getNextPageToken());
postListMutableLiveData.setValue(list);
}
if (token.getValue() == null || !token.getValue().equals("") ) {
finalURL.setValue(finalURL.getValue() + "&pageToken=" + token.getValue());
}
// Log.i(TAG,response.body().getItems().toString());
}
@Override
public void onFailure(Call<PostList> call, Throwable t) {
Log.e(TAG,t.getMessage());
}
});
}
我在我的应用程序中使用 blogger API、retrofit 和 MVVM,我试图在用户滚动时使用分页来加载更多帖子,这里发生的问题是响应正在自行加载“相同列表/相同的十个帖子再次加载
这是我的代码
PostsClient Class
public class PostsClient {
private static final String TAG = "PostsClient";
private static final String KEY = "XYZ sensitive key!";
private static final String BASE_URL = "https://www.googleapis.com/blogger/v3/blogs/4294497614198718393/";
private PostInterface postInterface;
private static PostsClient INSTANCE;
public PostsClient() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
postInterface = retrofit.create(PostInterface.class);
}
public static PostsClient getINSTANCE() {
if(INSTANCE == null){
INSTANCE = new PostsClient();
}
return INSTANCE;
}
public Call<PostList> getPostList(){
return postInterface.getPostList(KEY);
}
}
[PostViewModel]
public class PostViewModel extends ViewModel {
public static final String TAG = "PostViewModel";
public MutableLiveData<PostList> postListMutableLiveData = new MutableLiveData<>();
public MutableLiveData<PostList> postListByLabelMutableLiveData = new MutableLiveData<>();
public MutableLiveData<String> finalURL = new MutableLiveData<>();
public MutableLiveData<String> token = new MutableLiveData<>();
public void getPosts(){
if (token.getValue() != "") {
finalURL.setValue(finalURL.getValue() + "&pageToken=" + token.getValue());
}
if (token == null) {
return;
}
PostsClient.getINSTANCE().getPostList().enqueue(new Callback<PostList>() {
@Override
public void onResponse(@NotNull Call<PostList> call, @NotNull Response<PostList> response) {
PostList list = response.body();
if (list.getItems() != null) {
token.setValue(list.getNextPageToken());
postListMutableLiveData.setValue(list);
}
Log.i(TAG,response.body().getItems().toString());
}
@Override
public void onFailure(Call<PostList> call, Throwable t) {
Log.e(TAG,t.getMessage());
}
});
}
public void getPostListByLabel(){
PostsByLabelClient.getINSTANCE().getPostListByLabel(finalURL.getValue()).enqueue(new Callback<PostList>() {
@Override
public void onResponse(Call<PostList> call, Response<PostList> response) {
postListByLabelMutableLiveData.setValue(response.body());
}
@Override
public void onFailure(Call<PostList> call, Throwable t) {
}
});
}
}
HomeFragmentClass“主页”
public class HomeFragment extends Fragment {
private PostViewModel postViewModel;
public static final String TAG = "HomeFragment";
private RecyclerView recyclerView;
private PostAdapter postAdapter;
private List<Item> itemArrayList;
private boolean isScrolling = false;
private int currentItems, totalItems, scrollOutItems, selectedIndex;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
postViewModel = new ViewModelProvider(this).get(PostViewModel.class);
postViewModel.getPosts();
View root = inflater.inflate(R.layout.fragment_home, container, false);
itemArrayList = new ArrayList<>();
recyclerView = root.findViewById(R.id.homeRecyclerView);
postAdapter = new PostAdapter(getContext(),itemArrayList);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(linearLayoutManager);
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext()
, linearLayoutManager.getOrientation());
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.addItemDecoration(dividerItemDecoration);
recyclerView.setAdapter(postAdapter);
// textView.setText(s);
postViewModel.postListMutableLiveData.observe(HomeFragment.this, new Observer<PostList>() {
@Override
public void onChanged(PostList postList) {
itemArrayList.addAll(postList.getItems());
postAdapter.notifyDataSetChanged();
}
});
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
isScrolling = true;
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy > 0) {
currentItems = linearLayoutManager.getChildCount();
totalItems = linearLayoutManager.getItemCount();
scrollOutItems = linearLayoutManager.findFirstVisibleItemPosition();
if (isScrolling && (currentItems + scrollOutItems == totalItems)) {
isScrolling = false;
postViewModel.getPosts();
postAdapter.notifyDataSetChanged();
}
}
}
});
return root;
}
}
'更多解释
在 PostViewModel 上 我创建了一个变量
public MutableLiveData<String> token = new MutableLiveData<>();
这个代表新 page/response 的令牌将带有“每个页面都有一个列表/十个新帖子”
在 HomeFragment 上
我创建了三个整数值
private int currentItems, totalItems, scrollOutItems, selectedIndex;
和一个布尔值
private boolean isScrolling = false;
然后我用了recyclerView.addOnScrollListener
用这种方式加载接下来的十个帖子,但它不像我之前说的那样工作,加载相同 result/list
The result on imgur.com每次您想获取新帖子时,您都会更改 finalURL
值:
if (token.getValue() != "") {
finalURL.setValue(finalURL.getValue() + "&pageToken=" + token.getValue());
}
这里你使用 finalURL.getValue()
,它已经包含旧的令牌值(上一页的)。第一页就可以了。
当您返回下一页时,您将获得 finalURL
的值并将新标记连接到它,尽管当前 finalURL
已经包含最后一个标记的值。所以现在 finalURL
包含几个标记,我认为 API 可以采用第一个标记,即上一页的标记(因此,您将获得相同的帖子列表)。
因此您需要将其更改为 baseURL 的常量值:
final String baseURL = "" // add base URL which is basically the initial value of the `finalURL`
if (token.getValue() != "") {
finalURL.setValue(baseURL + "&pageToken=" + token.getValue());
}
旁注:
如果您打算将字符串与 token.getValue() != ""
进行比较,则需要将其更改为字符串 .equals()
或使用 isEmpty()
方法检查空字符串。
考虑使用新的分页库。这里有一个sample and the documentation。它的性能更好,您不必手动跟踪滚动的位置。通过遵循示例,我发现实现它相当容易,尽管它有很多样板代码。## Heading ##
经过数百次尝试,终于解决了,这里是问题的解决方案
首先 我更改了 API PostInterface
中的 GET
方法并使其采用 @URL
而不是 @Query
KEY 像这样
public interface PostInterface {
@GET
Call<PostList> getPostList(@Url String URL);
}
Secondary 我编辑了 PostsClient
从 BASE_URL
private static String BASE_URL
中删除的 final 并创建了 setter & getter 对于 BASE URL & KEY
public static String getKEY() {
return KEY;
}
public static String getBaseUrl() {
return BASE_URL;
}
第三次也是最后一次 我在响应后为令牌检查程序移动了这个 if 语句
public void getPosts(){
Log.e(TAG,finalURL.getValue());
PostsClient.getINSTANCE().getPostList(finalURL.getValue()).enqueue(new Callback<PostList>() {
@Override
public void onResponse(@NotNull Call<PostList> call, @NotNull Response<PostList> response) {
PostList list = response.body();
if (list.getItems() != null) {
Log.e(TAG,list.getNextPageToken());
token.setValue(list.getNextPageToken());
postListMutableLiveData.setValue(list);
}
if (token.getValue() == null || !token.getValue().equals("") ) {
finalURL.setValue(finalURL.getValue() + "&pageToken=" + token.getValue());
}
// Log.i(TAG,response.body().getItems().toString());
}
@Override
public void onFailure(Call<PostList> call, Throwable t) {
Log.e(TAG,t.getMessage());
}
});
}