Android: StaggeredGridLayoutManager scroll to top while initializing
Asked Answered
R

5

8

I'm using StaggeredGridLayoutManager for my image gallery. I've set setReverseLayout(true), so that images get stacked from bottom.

The problem I'm facing is that at initialization of app, the scroll points at bottom of gallery. I would want the scroll to be at the top of the gallery when user first starts the app.

I've tried using scrollToPosition (the following code snippet) but then the scroll ends up somewhere in middle of gallery, probably because the images haven't loaded up properly at the time of calling scrollToPosition.

mLayoutManager = new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL);
    mLayoutManager.setReverseLayout(true);

mRecyclerView.setLayoutManager(mLayoutManager);
mImageList = ((MainActivity)getActivity()).getImages();
adapter = new ImageAdapter(getActivity(),mImageList);
mRecyclerView.setAdapter(adapter);
mRecyclerView.scrollToPosition(mImageList.size()-1);

Is there a proper way to point the scroll at the top, rather than at the bottom?

Romaic answered 7/3, 2016 at 12:55 Comment(0)
M
2

I solved similar problem before and here is method:

mRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(
                new ViewTreeObserver.OnGlobalLayoutListener() {
                    @SuppressWarnings("deprecation")
                    @Override
                    public void onGlobalLayout() {
                        ViewTreeObserver observer = mRecyclerView.getViewTreeObserver();
                        scrollRecyclerViewToTop();
                        if (!observer.isAlive()) {
                            return;
                        }
                        if (mScrolledByUser) {
                            if (hasJellyBeanApi()) {
                                observer.removeOnGlobalLayoutListener(this);
                            } else {
                                observer.removeGlobalOnLayoutListener(this);
                            }
                        }
                    }
                });

And:

mRecyclerView.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        mScrollByUser = true;
        return false;
    }
});

And:

private void scrollRecyclerViewToTop() {
    mRecyclerView.scrollToPosition(0);
    mRecyclerView.scrollBy(0, Integer.MIN_VALUE);
}

The meaning is easy: let RecyclerView always scroll to top until user touches it. Hope this can help you.

Merissameristem answered 14/11, 2016 at 8:22 Comment(1)
it works when i touch the screen but nothing shows up if i dont touch. I want to scroll it by default that is user doesnt required to touch. This is the closest answer so i will award bounty for you. btw my answer works as i want.Perspex
T
1

try finding the first visible position by calling findFirstCompletelyVisibleItemPositions

or findFirstVisibleItemPositions

the call your recycler view scrollToPosition with the position obtained from the previous method(s)

Testimony answered 7/3, 2016 at 13:2 Comment(1)
That won't solve the problem completely. The first visible item will not always be the last item. So I may end up scrolling to different positions on different runs.Romaic
D
1

Try using scrollToPositionWithOffset() function of StaggeredGridLayoutManager instead. I found it more reliable than scrollToPosition. Also make sure that you execute this function inside Handler().post(). This will cause the Runnable to be added to the message queue. It will run once the UI thread is free.

new Handler().post(new Runnable() {
            @Override
            public void run() {
                staggeredGridLayoutManager.scrollToPositionWithOffset(mImageList.size() - 1, 0);
            }
        });
Designer answered 9/11, 2016 at 5:32 Comment(1)
Unfortunately it didnt work for me. Thansk for the answerPerspex
P
1

I used scrolltoposition inside dataobserver and now it works fine..

When the recyclerview loads, items on top are visible with the below code:

rcAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
            @Override
            public void onItemRangeInserted(int positionStart, int itemCount) {
                super.onItemRangeInserted(positionStart, itemCount);
                int count = rcAdapter.getItemCount();
                mRecyclerView.scrollToPosition(itemCount-1);

            }
        });

to scroll to the bottom for newly added items as in the chat view, use below code.

     rcAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
                    @Override
                    public void onItemRangeInserted(int positionStart, int itemCount) {
                        super.onItemRangeInserted(positionStart, itemCount);
                        int count = rcAdapter.getItemCount();
                        int lastVisiblePositions[] = new int[2]; //for 2 columns
                        int lastVisiblePosition = staggeredGridLayoutManager.findLastCompletelyVisibleItemPositions(lastVisiblePositions)[0];
                        // If the recycler view is initially being loaded or the user is at the bottom of the list, scroll
                        // to the bottom of the list to show the newly added message.
                        if (lastVisiblePosition == -1 ||
                            (positionStart >= (count- 1) && lastVisiblePosition == (positionStart - 1))) {
                           mRecyclerView.scrollToPosition(positionStart);
                        }

                    }
                });
Perspex answered 9/11, 2016 at 18:11 Comment(1)
Looks interesting.Romaic
H
0

This code may help you,I also used this link in code

https://guides.codepath.com/android/Endless-Scrolling-with-AdapterViews-and-RecyclerView

Every AdapterView (such as ListView and GridView) has support for binding to the OnScrollListener events which are triggered whenever a The user scrolls through the collection. Using this system, we can define a basic EndlessScrollListener which supports most use cases by creating our own class that extends OnScrollListener:

public abstract class EndlessScrollListener implements AbsListView.OnScrollListener {
    // The minimum number of items to have below your current scroll position
    // before loading more.
    private int visibleThreshold = 5;
    // The current offset index of data you have loaded
    private int currentPage = 0;
    // The total number of items in the dataset after the last load
    private int previousTotalItemCount = 0;
    // True if we are still waiting for the last set of data to load.
    private boolean loading = true;
    // Sets the starting page index
    private int startingPageIndex = 0;

    public EndlessScrollListener() {
    }

    public EndlessScrollListener(int visibleThreshold) {
        this.visibleThreshold = visibleThreshold;
    }

    public EndlessScrollListener(int visibleThreshold, int startPage) {
        this.visibleThreshold = visibleThreshold;
        this.startingPageIndex = startPage;
        this.currentPage = startPage;
    }

    // This happens many times a second during a scroll, so be wary of the code you place here.
    // We are given a few useful parameters to help us work out if we need to load some more data,
    // but first we check if we are waiting for the previous load to finish.
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) 
  {
        // If the total item count is zero and the previous isn't, assume the
        // list is invalidated and should be reset back to initial state
        if (totalItemCount < previousTotalItemCount) {
            this.currentPage = this.startingPageIndex;
            this.previousTotalItemCount = totalItemCount;
            if (totalItemCount == 0) { this.loading = true; } 
        }
        // If it's still loading, we check to see if the dataset count has
        // changed, if so we conclude it has finished loading and update the current page
        // number and total item count.
        if (loading && (totalItemCount > previousTotalItemCount)) {
            loading = false;
            previousTotalItemCount = totalItemCount;
            currentPage++;
        }

        // If it isn't currently loading, we check to see if we have breached
        // the visibleThreshold and need to reload more data.
        // If we do need to reload some more data, we execute onLoadMore to fetch the data.
        if (!loading && (firstVisibleItem + visibleItemCount + visibleThreshold) >= totalItemCount ) {
         loading = onLoadMore(currentPage + 1, totalItemCount);
        }
    }

    // Defines the process for actually loading more data based on page
    // Returns true if more data is being loaded; returns false if there is no more data to load.
    public abstract boolean onLoadMore(int page, int totalItemsCount);

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        // Don't take any action on changed
    }
  }
Hollo answered 15/11, 2016 at 17:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.