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

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


当前回答

大多数答案是假设RecyclerView使用LinearLayoutManager,或GridLayoutManager,甚至StaggeredGridLayoutManager,或假设滚动是垂直或水平的,但没有人张贴一个完全通用的答案。

使用ViewHolder的适配器显然不是一个好的解决方案。一个适配器可能有超过1个RecyclerView在使用它。它“改编”了它们的内容。它应该是RecyclerView(它是一个负责当前显示给用户的类,而不是适配器,它只负责向RecyclerView提供内容),它必须通知您的系统需要更多的项(加载)。

下面是我的解决方案,只使用了RecyclerView (RecycerView. view)的抽象类。LayoutManager和RecycerView.Adapter):

/**
 * Listener to callback when the last item of the adpater is visible to the user.
 * It should then be the time to load more items.
 **/
public abstract class LastItemListener extends RecyclerView.OnScrollListener {

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

      // init
      RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
      RecyclerView.Adapter adapter = recyclerView.getAdapter();

      if (layoutManager.getChildCount() > 0) {
        // Calculations..
        int indexOfLastItemViewVisible = layoutManager.getChildCount() -1;
        View lastItemViewVisible = layoutManager.getChildAt(indexOfLastItemViewVisible);
        int adapterPosition = layoutManager.getPosition(lastItemViewVisible);
        boolean isLastItemVisible = (adapterPosition == adapter.getItemCount() -1);

        // check
        if (isLastItemVisible)
          onLastItemVisible(); // callback
     }
   }

   /**
    * Here you should load more items because user is seeing the last item of the list.
    * Advice: you should add a bollean value to the class
    * so that the method {@link #onLastItemVisible()} will be triggered only once
    * and not every time the user touch the screen ;)
    **/
   public abstract void onLastItemVisible();

}


// --- Exemple of use ---

myRecyclerView.setOnScrollListener(new LastItemListener() {
    public void onLastItemVisible() {
         // start to load more items here.
    }
}

其他回答

我使用了recyclerview。adapter的onBindViewHolder方法。

适配器:

public interface OnViewHolderListener {
    void onRequestedLastItem();
}

@Override
public void onBindViewHolder(ViewHolder holder, int position) {

    ...

    if (position == getItemCount() - 1) onViewHolderListener.onRequestedLastItem();
}

片段(或活动):

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    contentView = inflater.inflate(R.layout.comments_list, container, false);
    recyclerView = (RecyclerView) mContentView.findViewById(R.id.my_recycler_view);
    adapter = new Adapter();
    recyclerView.setAdapter(adapter);

    ...

    adapter.setOnViewHolderListener(new Adapter.OnViewHolderListener() {
        @Override
        public void onRequestedLastItem() {
            //TODO fetch new data from webservice
        }
    });
    return contentView;
}

@kushal @abdulaziz

为什么不使用这个逻辑呢?

public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
    int totalItemCount, lastVisibleItemPosition;

    if (dy > 0) {
      totalItemCount = _layoutManager.getItemCount();
      lastVisibleItemPosition = _layoutManager.findLastVisibleItemPosition();

      if (!_isLastItem) {
        if ((totalItemCount - 1) == lastVisibleItemPosition) {
          LogUtil.e("end_of_list");

          _isLastItem = true;
        }
      }
    }
  }

也可以在没有滚动侦听器的情况下实现,只使用数据模型的纯逻辑。滚动视图需要按位置获取项目以及最大项目计数。模型可以具有后台逻辑,以块的形式获取所需的项,而不是一个一个地获取,并在后台线程中执行此操作,在数据准备就绪时通知视图。

这种方法允许获取队列优先选择最近请求的(所以当前可见)项而不是旧的(很可能已经滚动了)提交,控制使用的并行线程的数量等等。这个方法的完整源代码(演示应用程序和可重用库)可以在这里找到。

没有重复调用,但你滚动。fetchData只会在您位于列表的末尾并且有更多数据要获取时调用

关于我的代码: 1.fetchData()每次调用取10个项。 2.isScrolling是一个类成员变量 3.previousTotalCount也是类成员。它存储以前访问过的最后一个项目

recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
            override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
                super.onScrollStateChanged(recyclerView, newState)
                if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
                    isScrolling = true
                }
            }

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

                val visibleItemCount = layoutManager.childCount
                val totalItemCount = layoutManager.itemCount
                val scrolledOutItems = layoutManager.findFirstVisibleItemPosition()


                if (isScrolling && (visibleItemCount + scrolledOutItems == totalItemCount)
                    && (visibleItemCount + scrolledOutItems != previousTotalCount)) {
                    previousTotalCount = totalItemCount              
                    isScrolling = false
                    fetchData()  //fetches 10 new items from Firestore
                    pagingProgressBar.visibility = View.VISIBLE
                }
            }
        })

大多数答案是假设RecyclerView使用LinearLayoutManager,或GridLayoutManager,甚至StaggeredGridLayoutManager,或假设滚动是垂直或水平的,但没有人张贴一个完全通用的答案。

使用ViewHolder的适配器显然不是一个好的解决方案。一个适配器可能有超过1个RecyclerView在使用它。它“改编”了它们的内容。它应该是RecyclerView(它是一个负责当前显示给用户的类,而不是适配器,它只负责向RecyclerView提供内容),它必须通知您的系统需要更多的项(加载)。

下面是我的解决方案,只使用了RecyclerView (RecycerView. view)的抽象类。LayoutManager和RecycerView.Adapter):

/**
 * Listener to callback when the last item of the adpater is visible to the user.
 * It should then be the time to load more items.
 **/
public abstract class LastItemListener extends RecyclerView.OnScrollListener {

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

      // init
      RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
      RecyclerView.Adapter adapter = recyclerView.getAdapter();

      if (layoutManager.getChildCount() > 0) {
        // Calculations..
        int indexOfLastItemViewVisible = layoutManager.getChildCount() -1;
        View lastItemViewVisible = layoutManager.getChildAt(indexOfLastItemViewVisible);
        int adapterPosition = layoutManager.getPosition(lastItemViewVisible);
        boolean isLastItemVisible = (adapterPosition == adapter.getItemCount() -1);

        // check
        if (isLastItemVisible)
          onLastItemVisible(); // callback
     }
   }

   /**
    * Here you should load more items because user is seeing the last item of the list.
    * Advice: you should add a bollean value to the class
    * so that the method {@link #onLastItemVisible()} will be triggered only once
    * and not every time the user touch the screen ;)
    **/
   public abstract void onLastItemVisible();

}


// --- Exemple of use ---

myRecyclerView.setOnScrollListener(new LastItemListener() {
    public void onLastItemVisible() {
         // start to load more items here.
    }
}