我想把ListView改为RecyclerView。我想在RecyclerView中使用OnScrollListener的onScroll来确定用户是否滚动到列表的末尾。

我如何知道用户是否滚动到列表的末尾以便我可以从REST服务获取新数据?


当前回答

这是另一种方法。它将与任何布局管理器工作。

使Adapter类抽象 然后在适配器类中创建一个抽象方法。load ()) 在onBindViewHolder中检查位置,如果最后调用load() 在活动或片段中创建适配器对象时重写load()函数。 在重载加载函数实现你的loadmore调用

为了详细了解,我写了一篇博客文章和示例项目 http://sab99r.com/blog/recyclerview-endless-load-more/

MyAdapter.java

public abstract class MyAdapter extends RecyclerView.Adapter<ViewHolder>{

        @Override
        public void onBindViewHolder(ViewHolder holder, int position) {
            //check for last item
            if ((position >= getItemCount() - 1))
                load();
        }

        public abstract void load();
}

MyActivity.java

public class MainActivity extends AppCompatActivity {
    List<Items> items;
    MyAdapter adapter;

   @Override
    protected void onCreate(Bundle savedInstanceState) {
    ...
    adapter=new MyAdapter(items){
            @Override
            public void load() {
                //implement your load more here
                Item lastItem=items.get(items.size()-1);
                loadMore();
            }
        };
   }
}

其他回答

有一个方法public void setOnScrollListener (RecyclerView。OnScrollListener监听器)在https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html#setOnScrollListener%28android.support.v7.widget.RecyclerView.OnScrollListener%29。使用这个

编辑:

在onScrollListener中重写onScrollStateChanged方法并执行此操作

            boolean loadMore = firstVisibleItem + visibleItemCount >= totalItemCount;

            //loading is used to see if its already loading, you have to manually manipulate this boolean variable
            if (loadMore && !loading) {
                 //end of list reached
            }

借助Kotlin扩展函数的强大功能,代码可以看起来更加优雅。把它放在你想要的任何地方(我把它放在一个ExtensionFunctions中。kt文件):

/**
 * WARNING: This assumes the layout manager is a LinearLayoutManager
 */
fun RecyclerView.addOnScrolledToEnd(onScrolledToEnd: () -> Unit){

    this.addOnScrollListener(object: RecyclerView.OnScrollListener(){

        private val VISIBLE_THRESHOLD = 5

        private var loading = true
        private var previousTotal = 0

        override fun onScrollStateChanged(recyclerView: RecyclerView,
                                          newState: Int) {

            with(layoutManager as LinearLayoutManager){

                val visibleItemCount = childCount
                val totalItemCount = itemCount
                val firstVisibleItem = findFirstVisibleItemPosition()

                if (loading && totalItemCount > previousTotal){

                    loading = false
                    previousTotal = totalItemCount
                }

                if(!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)){

                    onScrolledToEnd()
                    loading = true
                }
            }
        }
    })
}

然后像这样使用它:

youRecyclerView.addOnScrolledToEnd {
    //What you want to do once the end is reached
}

这个解决方案基于Kushal Sharma的回答。然而,这是一个更好的,因为:

它使用onScrollStateChanged而不是onScroll。这样做比较好,因为每当RecyclerView中有任何类型的移动时,onScroll都会被调用,而onScrollStateChanged只在RecyclerView的状态改变时被调用。使用onScrollStateChanged将节省CPU时间,从而节省电池。 因为它使用扩展函数,所以可以在任何RecyclerView中使用。客户端代码只有一行。

检查的每一件事都有详细的解释: 使用RecyclerView从A到Z分页

    mRecyclerView.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 = mLayoutManager.getChildCount();
        int totalItemCount = mLayoutManager.getItemCount();
        int firstVisibleItemPosition = mLayoutManager.findFirstVisibleItemPosition();

        if (!mIsLoading && !mIsLastPage) {
            if ((visibleItemCount + firstVisibleItemPosition) >= totalItemCount
                    && firstVisibleItemPosition >= 0) {
                loadMoreItems();
            }
        }
    }
})

loadMoreItems ():

private void loadMoreItems() {
    mAdapter.removeLoading();
    //load data here from the server

    // in case of success
    mAdapter.addData(data);
    // if there might be more data
    mAdapter.addLoading();
}

在MyAdapter中:

private boolean mIsLoadingFooterAdded = false;

public void addLoading() {
    if (!mIsLoadingFooterAdded) {
        mIsLoadingFooterAdded = true;
        mLineItemList.add(new LineItem());
        notifyItemInserted(mLineItemList.size() - 1);
    }
}

public void removeLoading() {
    if (mIsLoadingFooterAdded) {
        mIsLoadingFooterAdded = false;
        int position = mLineItemList.size() - 1;
        LineItem item = mLineItemList.get(position);

        if (item != null) {
            mLineItemList.remove(position);
            notifyItemRemoved(position);
        }
    }
}

public void addData(List<YourDataClass> data) {

    for (int i = 0; i < data.size(); i++) {
        YourDataClass yourDataObject = data.get(i);
        mLineItemList.add(new LineItem(yourDataObject));
        notifyItemInserted(mLineItemList.size() - 1);
    }
}

这些答案都没有考虑到列表是否太小。

这是我一直在使用的一段代码,它在两个方向上都适用于RecycleViews。

@Override
    public boolean onTouchEvent(MotionEvent motionEvent) {

        if (recyclerViewListener == null) {
            return super.onTouchEvent(motionEvent);
        }

        /**
         * If the list is too small to scroll.
         */
        if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
            if (!canScrollVertically(1)) {
                recyclerViewListener.reachedBottom();
            } else if (!canScrollVertically(-1)) {
                recyclerViewListener.reachedTop();
            }
        }

        return super.onTouchEvent(motionEvent);
    }

    public void setListener(RecyclerViewListener recycleViewListener) {
        this.recyclerViewListener = recycleViewListener;
        addOnScrollListener(new OnScrollListener() {

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

                if (recyclerViewListener == null) {
                    return;
                }

                recyclerViewListener.scrolling(dy);

                if (!canScrollVertically(1)) {
                    recyclerViewListener.reachedBottom();
                } else if (!canScrollVertically(-1)) {
                    recyclerViewListener.reachedTop();
                }
            }

        });
    }

做这些变量。

private int previousTotal = 0;
private boolean loading = true;
private int visibleThreshold = 5;
int firstVisibleItem, visibleItemCount, totalItemCount;

设置为滚动回收视图。

mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

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

        visibleItemCount = mRecyclerView.getChildCount();
        totalItemCount = mLayoutManager.getItemCount();
        firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition();

        if (loading) {
            if (totalItemCount > previousTotal) {
                loading = false;
                previousTotal = totalItemCount;
            }
        }
        if (!loading && (totalItemCount - visibleItemCount) 
            <= (firstVisibleItem + visibleThreshold)) {
            // End has been reached

            Log.i("Yaeye!", "end called");

            // Do something

            loading = true;
        }
    }
});

注意:确保你使用LinearLayoutManager作为RecyclerView的布局管理器。

LinearLayoutManager mLayoutManager;
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);

对于网格来说

GridLayoutManager mLayoutManager;
mLayoutManager = new GridLayoutManager(getActivity(), spanCount);
mRecyclerView.setLayoutManager(mLayoutManager);

有乐趣与你无尽的卷轴!!^ ^。

更新:mRecyclerView.setOnScrollListener()已弃用,只需替换为mRecyclerView.addOnScrollListener(),警告将消失!你可以从这个SO问题中读到更多。

由于Android现在正式支持Kotlin,这里是相同的更新-

使OnScrollListener

class OnScrollListener(val layoutManager: LinearLayoutManager, val adapter: RecyclerView.Adapter<RecyclerAdapter.ViewHolder>, val dataList: MutableList<Int>) : RecyclerView.OnScrollListener() {
    var previousTotal = 0
    var loading = true
    val visibleThreshold = 10
    var firstVisibleItem = 0
    var visibleItemCount = 0
    var totalItemCount = 0

    override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
        super.onScrolled(recyclerView, dx, dy)

        visibleItemCount = recyclerView.childCount
        totalItemCount = layoutManager.itemCount
        firstVisibleItem = layoutManager.findFirstVisibleItemPosition()

        if (loading) {
            if (totalItemCount > previousTotal) {
                loading = false
                previousTotal = totalItemCount
            }
        }

        if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) {
            val initialSize = dataList.size
            updateDataList(dataList)
            val updatedSize = dataList.size
            recyclerView.post { adapter.notifyItemRangeInserted(initialSize, updatedSize) }
            loading = true
        }
    }
}

然后像这样把它添加到你的RecyclerView中

recyclerView.addOnScrollListener(OnScrollListener(layoutManager, adapter, dataList))

要获得完整的代码示例,请参阅Github repo。