14

In a normal recyclerview, the newest items are all at the top and if you add an item into the recyclerview, it pushes the existing items down and the new item takes the top position.

In a reverseLayout recyclerview, the newest items are all at the bottom and if you add an item into the recyclerview, it is added right at the bottom. This kind of layout is quite useful for things like comment feeds where you expect the latest comments to be at the bottom.

A good example of this is the Facebook comment feed when you comment on a friend's status for example. You can see that the comments all have the time that they were posted at the bottom and the times are decreasing from 12 hours to 10 hours from top to bottom:

facebook comment feed

The reverseLayout is quite easy to set and I have done it using this code:

mLayoutManager = new LinearLayoutManager(this);
mLayoutManager.setReverseLayout(true);
mLayoutManager.setStackFromEnd(true);

I'm however struggling to attach a reverse endless scroll listener. I'm using the following Vilen's code from my question a while back (Adding items to Endless Scroll RecyclerView with ProgressBar at bottom) and modifying it so that it the onLoadMoreListener will only activate itself when it is scrolling up instead of down. You want to scroll up to older comments:

    if (recyclerView.getLayoutManager() instanceof LinearLayoutManager) {
        final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
        //if you add on addOnScrollListener, it will cause the scroll listener to be called twice each time you reset your adapter
        recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
            int firstVisibleItem;
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);

                final int currentFirstVisibleItem = linearLayoutManager.findFirstVisibleItemPosition();
                totalItemCount = linearLayoutManager.getItemCount();
                lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();

                if(lastVisibleItem > firstVisibleItem)
                {
                    Log.i("SCROLLING UP","TRUE");
                    if (!loading && totalItemCount <= (lastVisibleItem + visibleThreshold)) {
                        current_page++;
                        if (onLoadMoreListener != null) {
                            onLoadMoreListener.onLoadMore(current_page);
                        }
                        loading = true;
                    }
                }
                lastVisibleItem=firstVisibleItem;
            }
        });
    }

When the onLoadMoreListener is called, the following code is called in my activity class:

    CommentsAdapter.OnLoadMoreListener onLoadMoreListener = new CommentsAdapter.OnLoadMoreListener() {
    @Override
    public void onLoadMore(int current_page) {
        //get the firstCommentId on the list of comments so that we can send it to the server to fetch comments less than that comment id to display in our recyclerview
        firstCommentId = comments.get(0).getId();
        //add empty item for progress bar / wheel and display the progress bar / wheel
        Comment comment = new Comment();
        comment.setViewType(Constants.COMMENT_PROGRESS_BAR);
        comments.add(0, comment);
        mCommentAdapter.notifyItemInserted(0);
        mCommentAdapter.notifyItemRangeChanged(1, comments.size());

        //CODE FOR NETWORK OPERATIONS GOES HERE TO FETCH THE NEXT FEW COMMENTS WHEN SCROLLING UP//
    }
};

However, the onLoadMoreListener doesn't seem to be called at all when I scroll up.

I also made sure I had at least 10 comments in my feed and 5 was loaded before and 5 more should be loaded when I scroll up.

Does anything know what I'm doing wrong with this reverse OnLoadMoreListener?

EDIT:

The reverseLayout recyclerview actually does work with the original code from Vilen, I have uploaded a github repo to show this:

https://github.com/Winghin2517/ReverseLayoutRecyclerview

There is still an issue with when the onLoadMoreListener is activated though - If the comment list only comprises of 3 comments and there is no more comments to add on, I don't want the onLoadMoreListener to activate at all when I initially view my comments - when I first click on my app to see the comments. Right now, when I click to view my comments, even though there are 3 comments in the Dataset, the progressBar is still displayed and the disappears after a 2 seconds which I think is not necessary (see github repo where I demonstrate this).

Disabling it when there is only 3 comments is also not a good idea as the onLoadMoreListener acts as like a swipeToRefresh object. If there are more comments from the user subsequent to when he views the comment feed, he can swipe down and the onLoadMoreListener will display those comments.

Is there a way to just disable it when the feed loads initially?

Community
  • 1
  • 1
Simon
  • 18,312
  • 22
  • 130
  • 197
  • hi Simon, I will take a look once I get few minutes free time. Mean time make sure that you are not using last visible item but instead trigger refresh when current visible item position is 1 or 0 – Vilen Dec 18 '15 at 17:33
  • Thanks Vilen. I have been trying different things but unfortunately i still cannot get it to work properly. I have added on a bounty to sweeten the deal. – Simon Dec 20 '15 at 13:25

2 Answers2

11

Simon: solution I previously proposed here works fine with reverse layout without any modification. The only thing I'd add for reverse layout is to scroll automatically to first item when you show it first time but that's up to you. Now getting back to issue you have. You mentioned that when you scroll nothing happens. so my guess is that you initialize your recylcer view in a wrong order. Make sure you do it like this

mLayoutManager = new LinearLayoutManager(this);
mLayoutManager.setReverseLayout(true);
mLayoutManager.setStackFromEnd(true);

mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter = new MyAdapter<>(myDataset, mRecyclerView);
mRecyclerView.setAdapter(mAdapter);

Note that layout manager gets instantiated first, then you set it and after that you provide adapter. Let me know if that was the case.

Edit Just bringing up this from comment below:

forget about what we have with onLoadMoreListener and all scrolling stuff just use standard RecyclerView wrapped in SwipeToRefreshLayout

Community
  • 1
  • 1
Vilen
  • 4,973
  • 3
  • 24
  • 38
  • Hi Vilen, I actually took your initial code and just put in it into a new android project and it seems to work with the reverse layout for recyclerview. I think I was just confused as I though the order of the data would change, eg, instead of comments.add(0, comment);, I would add on the comments.add(comments.size(), comment); as the list was reversed. All very confusing. I do have an extra question on your code which seems to impact the reverseLayout recyclerview when it is loaded initially. Can you please check out my edit? – Simon Dec 23 '15 at 08:15
  • Hi Simon, there is nothing confusing with comments.add(0, comment) revers layout means only layout revers not data set revers, it only starts rendering backward. Regarding to your edit I am not sure what behavior you actually need. if there is 3 items what you want it to do ? and what if subsequently there is more items how your program will know that ? do you want it to load more or not. and when exactly you need more items to be loaded ? – Vilen Dec 25 '15 at 05:46
  • So what I mean is if you have less than the amount of items to trigger the onLoadMoreListener, the progressBar shouldn't be displayed at all on initial load. In my github repo, we only have 3 items initially, so i did not expect to see the progress bar at all as there is no more items to load. If there are subsequently more items, then i would want to drag the recyclerview down manually which would trigger the listener to load more. The onLoadMoreListener should only be activated when you manually drag it down, not when you open the app. – Simon Dec 25 '15 at 10:23
  • 1
    ok then forget about what we have with onLoadMoreListener and all scrolling stuf just use standard recycler view wrapped in SwipeToRefreshLayout – Vilen Dec 25 '15 at 13:01
  • I think taking out the onLoadMoreListener worked better for me - thanks anyway. I do have another question the listener, do you mind having a look at it here: http://stackoverflow.com/questions/34953260/endless-scroll-recyclerview-with-desimilar-array-sizes-from-api? Its about dissimilar sizes of dataset which are returned in the real world by an API – Simon Jan 22 '16 at 18:12
0

I didn't really want to use a SwipeToRefreshLayout at the end as it doens't look good when you have a comment feed.

So what I did was modify the onLoadMoreListener method so that it detects when the actiivity is first started up. If it is first starting up, we do not want the progress bar to be displayed:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
            if (savedInstanceState == null) {
                                initial = true;
            }
}

Then we will use the initial boolean variable to determine whether the activity has just started up and disable the progress bar.

    @Override
    public void onLoadMore(int current_page) {
        //add progress item
        if (initial) {
            //we do not add the progress bar when the activity opens initially so
            //we do nothing. We just change the variable to false so going forward, you will add a progress bar each time you refresh
            initial = false;
        } else {
            comments.add(createProgressBar());
            mCommentAdapter.notifyItemInserted(comments.size()-1);
        }
Simon
  • 18,312
  • 22
  • 130
  • 197