Shagun Kazan
Shagun Kazan

Reputation: 126

RecyclerView findLastCompletelyVisibleItemPosition always returns last element

first question on StackOverflow!

I have an endlessly scrolling RecyclerView, preceded by several other views, all within a NestedScrollView.

Whenever I call findLastCompletelyVisibleItemPosition on the RecyclerView's layout manager, I am given the position of the last element loaded in the RecyclerView...even if only the first one is visible on screen. I believe this is due to the fact that the RecyclerView is within a NestedScrollView -- the item is being displayed, however I'd have to scroll down to view it -- but I could definitely be wrong.

Initialization of RecyclerView in Fragment OnViewCreated :

    @Override
    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

    //... irrelevant code ... //

   photosRecyclerView = view.findViewById(R.id.profile_recyclerview_photos);
   // Add LayoutManager to RecyclerView.
   GridLayoutManager manager = new GridLayoutManager(getContext(), 2);
   photosRecyclerView.setLayoutManager(manager);
   // Smoothes scrolling from NestedScrollView.
   photosRecyclerView.setNestedScrollingEnabled(false);
   // Add adapter to RecyclerView.
   profileRecyclerViewAdapter = new ProfileRecyclerViewAdapter(getContext(), photoList);
   photosRecyclerView.setAdapter(profileRecyclerViewAdapter);

ScrollListener attached to RecyclerView :


    private void initScrollListener() {

        photosRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
            }

            @Override
            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);

                final GridLayoutManager gridLayoutManager = (GridLayoutManager) recyclerView.getLayoutManager();

                Log.d(TAG, "" +  gridLayoutManager.findLastCompletelyVisibleItemPosition());

                if (!loadingPhotos) {
                  if (gridLayoutManager.findLastCompletelyVisibleItemPosition() == photoList.size()-1  && hasMorePhotos) {
                        Log.d(TAG, "Attempting to load more images.");
                        loadMore();
                        loadingPhotos = true;
                    }

                }
            }

        });
    }

Layout of the Fragment :

<androidx.core.widget.NestedScrollView android:layout_height="match_parent"
    android:layout_width="match_parent"
    xmlns:android="http://schemas.android.com/apk/res/android">

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/colorPrimaryDark"
    tools:context=".ProfileFragment">


  <------- RANDOM OTHER VIEWS -------->

   <androidx.recyclerview.widget.RecyclerView
      android:id="@+id/profile_recyclerview_photos"
      android:layout_marginTop="10dp"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      app:layout_constraintStart_toStartOf="@+id/profile_guideline_verticalstart"
      app:layout_constraintEnd_toEndOf="@+id/profile_guideline_verticalend"
      app:layout_constraintTop_toBottomOf="@id/profile_textview_photoslabel"/>

  </androidx.constraintlayout.widget.ConstraintLayout>

</androidx.core.widget.NestedScrollView>

Your help would be greatly appreciated!!

Upvotes: 6

Views: 2443

Answers (4)

Matboyi
Matboyi

Reputation: 19

Had the same problem, I fixed my problem by detecting when nestedScrollView is at the end!

nestedScrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
    @Override
    public void onScrollChanged() {
        if (nestedScrollView != null) {
            if (nestedScrollView.getChildAt(0).getBottom() <= (nestedScrollView.getHeight() + nestedScrollView.getScrollY())) {
                //scroll view is at bottom
                if (isAnythingToLoad && rowsArrayList.size() > 8) {
                    //bottom of list!
                    loadMore();
                }
            }
        }
    }
})

Upvotes: 0

Dev Arogundade
Dev Arogundade

Reputation: 11

I recently faced an issue with recycler view, tried every possible solution but still failed. So I came up with this lightweight solution.

binding.currencies.apply {
        adapter = currencyPagingAdapter
    }.addOnScrollListener(object : RecyclerView.OnScrollListener() {
        override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
            super.onScrollStateChanged(recyclerView, newState)

            val lm = recyclerView.layoutManager as LinearLayoutManager
            val lv = lm.getChildAt(lm.itemCount - 1)

            val scrollBounds = Rect()
            recyclerView.getHitRect(scrollBounds)

            if (lv != null) {
                if (lv.getLocalVisibleRect(scrollBounds)) {
                    Log.d("TAG", "onScrollStateChanged: visible")
                } else {
                    Log.d("TAG", "onScrollStateChanged: not visible")
                }

            }

        }
    })

Upvotes: 1

Abdelrahman MOhamed
Abdelrahman MOhamed

Reputation: 41

You can try using constraint layout and change the recyclerview height to 0dp .

It will solve your problem

Upvotes: 4

Shagun Kazan
Shagun Kazan

Reputation: 126

After some research -- with a lot of help coming from this answer, I realized that this behaviour is due to the fact that the RecyclerView height is set to wrap content. This causes it to load all of the items immediately and thus the last "completely visible" item is the last item in the RecyclerView.

Upvotes: 5

Related Questions