我如何确定 RecyclerView 最后一项在屏幕上可见?

How can I identify that RecyclerView's last item is visible on screen?

我有一个 RecyclerView,我在 RecyclerView 中添加了数据列表。当屏幕上显示最后一个 RecyclerView 项时,我想在列表中添加更多数据。之后我想进行网络服务调用并更新 RecyclerView 数据。我怎样才能做到这一点?

有什么建议吗?

一个选项涉及编辑您的 LayoutManager。这里的想法是找到最后一个可见项目的位置。如果该位置等于数据集的最后一项,那么您应该触发重新加载。

    @Override
    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {

        final int result = super.scrollVerticallyBy(dy, recycler, state);

        if (findLastVisibleItemPosition() == mData.length - 1) {
           loadMoreData();
        } 

        return result;

    }

    @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        super.onLayoutChildren(recycler, state);

        if (findLastVisibleItemPosition() == mData.length - 1) {
           loadMoreData();
        } 
    }

或者,您可以通过适配器的 onBindViewHolder 方法执行此操作,尽管这确实有点 "hack":

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (position == mData.length - 1) {
            // load more data here.
        }

        /// binding logic
    }

第三个选项是将 OnScrollListener 添加到 RecyclerView。 @velval 在此页面上的回答很好地解释了这一点。

无论您选择哪个选项,您还应该包含代码以防止数据加载逻辑触发太多次(例如,在先前获取更多数据的请求完成和 returns 新数据之前)。

如果有人偶然发现了这个 post this is 一个快速简单的教程:

您只需:

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

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        int visibleItemCount        = lm.getChildCount();
        int totalItemCount          = lm.getItemCount();
        int firstVisibleItemPosition= lm.findFirstVisibleItemPosition();

        // Load more if we have reach the end to the recyclerView
        if ( (visibleItemCount + firstVisibleItemPosition) >= totalItemCount && firstVisibleItemPosition >= 0) {
            loadMoreItems();
        }
    }
});

然后你的 loadMoreItems() 应该看起来像这样:

private void loadMoreItems() {
    // init offset=0 the frist time and increase the offset + the PAGE_SIZE when loading more items
    queryOffset = queryOffset + PAGE_SIZE;
    // HERE YOU LOAD the next batch of items
    List<Items> newItems = loadItems(queryOffset, PAGE_SIZE);

    if (newItems.size() > 0) {
        items.addAll(newItems);
        adapter.notifyDataSetChanged();
    }
}

看到上面的许多答案,但我的答案不同,它也适用于您的情况。我的方法是基于 recylerview 的滚动状态。维护下面的变量 "check",这应该只在 api 响应时更新一次。在您的 api 回复中放入以下代码。如果您只想在每次调用 api.

时处理最后一项
final boolean[] check = {true};
    recyclerView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
        @Override
        public void onScrollChanged() {
            if (!recyclerView.canScrollVertically(1)) {
                // last item of recylerview reached.
                if (check[0]) {
                    //your code for last reached item
                    scroll_handler.setVisibility(View.GONE);
                }
            } else {
                scroll_handler.setVisibility(View.VISIBLE);
                check[0] = false;
            }
        }
    });

如果你想每次都处理你的最后一个项目,那么按照下面的方式进行

recyclerView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
    @Override
    public void onScrollChanged() {
        if (!recyclerView.canScrollVertically(1)) 
            // Bottom of recyler view.
            arrow_img.setRotation(180);
        }
    }
});

另见

private fun isLastItemVisible(): Boolean {
    val layoutManager = recycler_view.layoutManager
    val position = layoutManager.findLastCompletelyVisibleItemPosition()
    return position >= adapter.itemCount - 1
}