Search code examples
androidmvvmandroid-recyclerviewretrofitinfinite-scroll

recyclerView.addOnScrollListener - "retrofit pagination with MVVM" is loading the same response/list


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.com

Solution

  • After 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());
                }
            });
    
        }