I am using blogger API, retrofit, and MVVM in my app, I trying to use pagination to load more posts when user is scrolling, the problem happening here the response is loading it self "the same list / same ten posts is loading again"
here's my code
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) {
}
});
}
}
HomeFragment Class "The main page"
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;
}
}
’More explanation
on PostViewModel I created one variable
public MutableLiveData<String> token = new MutableLiveData<>();
This token that represents a new page/response will carry "each page has a list / ten new posts"
on HomeFragment
I created three integer values
private int currentItems, totalItems, scrollOutItems, selectedIndex;
and one boolean
private boolean isScrolling = false;
then I used recyclerView.addOnScrollListener
with this way to load the next ten posts, but it's not working like I said before, its loading the same result/list
The result on imgur.comAfter hundreds of tries, I finally solved it, here's the solution of problem
Firstly I changed the GET
method in the API PostInterface
and make it take @URL
instead of @Query
KEY like this
public interface PostInterface {
@GET
Call<PostList> getPostList(@Url String URL);
}
Secondary I edited the PostsClient
removed final from BASE_URL
private static String BASE_URL
and create a setter & getter for BASE URL & KEY
public static String getKEY() {
return KEY;
}
public static String getBaseUrl() {
return BASE_URL;
}
Thirdly & finally I moved this if statement for the token checker after the response
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());
}
});
}
You mutate the finalURL
value each time you want to grab new posts with:
if (token.getValue() != "") {
finalURL.setValue(finalURL.getValue() + "&pageToken=" + token.getValue());
}
Here you use finalURL.getValue()
which already contains the old token value (of the previous page). This is OK for the first page.
When you come back for the next page, you get the value of the finalURL
and concatenate the new token to it, although the current finalURL
already contains the value of the last token. So now finalURL
contains a couple of tokens, I think the API can take the first one which is the toke of the previous page (So, you'll get the same list of posts).
So you need to change that with a constant value of the 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());
}
Side Note:
If you intend to compare Strings with token.getValue() != ""
, then you need to change that with String .equals()
or for checking empty String use isEmpty()
method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With