15

我正在尝试使用 recyclerView 创建消息类型的屏幕,该屏幕将从底部开始,并在用户到达聊天的顶端时加载更多数据。但我正面临这个奇怪的问题。

我的 recyclerView 在调用 notifyDataSetChanged 时滚动到顶部。由于这个 onLoadMore 被多次调用。

这是我的代码:

LinearLayoutManager llm = new LinearLayoutManager(context);
llm.setOrientation(LinearLayoutManager.VERTICAL);
llm.setStackFromEnd(true);
recyclerView.setLayoutManager(llm);

** 在适配器中

 @Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    if (messages.size() > 8 && position == 0 && null != mLoadMoreCallbacks) {
        mLoadMoreCallbacks.onLoadMore();
    }

** 在活动中

@Override
public void onLoadMore() {
    // Get data from database and add into arrayList
      chatMessagesAdapter.notifyDataSetChanged();
}

只是 recyclerView 滚动到顶部。如果滚动到顶部停止,此问题将得到解决。请帮我找出这个问题的原因。提前致谢。

4

10 回答 10

5

我认为您不应该那样使用 onBindViewHolder ,删除该代码,适配器应该只绑定模型数据,而不是监听滚动。

我通常以这种方式执行“onLoadMore”:

在活动中:

private boolean isLoading, totallyLoaded; //
RecyclerView mMessages;
LinearLayoutManager manager;
ArrayList<Message> messagesArray;
MessagesAdapter adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //...
    mMessages.setHasFixedSize(true);
    manager = new LinearLayoutManager(this);
    manager.setStackFromEnd(true);
    mMessages.setLayoutManager(manager);
    mMessages.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);

            if (manager.findFirstVisibleItemPosition() == 0 && !isLoading && !totallyLoaded) {
                onLoadMore();
                isLoading = true;
            }
        }
    });
    messagesArray = new ArrayList<>();
    adapter = new MessagesAdapter(messagesArray, this);
    mMessages.setAdapter(adapter);
}


@Override
public void onLoadMore() {
    //get more messages...
    messagesArray.addAll(0, moreMessagesArray);
    adapter.notifyItemRangeInserted(0, (int) moreMessagesArray.size();
    isLoading = false;
}

这对我来说非常有效,如果服务器不返回更多消息,则使用“totallyLoaded”来停止进行服务器调用。希望它可以帮助你。

于 2017-04-19T06:48:25.393 回答
2

List您会看到,当您插入新项目时,滚动到最顶部的项目是很自然的。好吧,您朝着正确的方向前进,但我认为您忘记添加setReverseLayout(true).

这里setStackFromEnd(true)只是告诉List从视图底部开始堆叠项目,但是当与它结合使用时,setReverseLayout(true)它将反转项目和视图的顺序,因此最新的项目总是显示在视图的底部。

你最终的 layoutManager 看起来像这样:

        mLayoutManager = new LinearLayoutManager(getActivity());
        mLayoutManager.setReverseLayout(true);
        mLayoutManager.setStackFromEnd(true);
        mRecyclerView.setLayoutManager(mLayoutManager);
于 2017-04-16T06:18:27.997 回答
1

这是我避免滚动视图移到顶部的方法,而不是使用notifyDataSetChanged(),我使用notifyItemRangeChanged();

List<Object> tempList = new ArrayList<>();
tempList.addAll(mList);
mList.clear();
mList.addAll(tempList);
notifyItemRangeChanged(0, mList.size());

更新:出于另一个原因,您在顶部的另一个视图正在聚焦,因此当您调用任何通知时它会跳转到顶部,因此通过添加android:focusableInTouchMode="true"GroupView 来删除所有焦点。

于 2019-06-07T05:06:43.603 回答
1

不要调用notifyDataSetChanged(). RecyclerView使用新的方法,如notifyItemChanged(), notifyItemRangeChanged(), notifyItemInserted(), 等等......如果你使用notifyItemRangeInserted()-

之后不要调用setAdapter()方法..!

于 2017-04-16T07:16:21.957 回答
0

你有这个问题,因为每次你的条件为真时,你调用的loadMore方法甚至loadMore处于运行状态,为了解决这个问题,你必须在你的代码中放入一个布尔值并检查它。

检查我的以下代码以获得更清晰的信息。

1- 在适配器类中声明一个布尔值

2-在您的情况下将其设置为 true

3-在您从数据库中获取数据并通知您的适配器后,将其设置为 false。

所以你的代码必须像下面的代码:

public class YourAdapter extend RecylerView.Adapter<.....> {

   private boolean loadingDataInProgress = false;


   public void setLoadingDataInProgress(boolean loadingDataInProgress) {
       this.loadingDataInProgress = loadingDataInProgress
   }


   .... 
   // other code


   @Override
   public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
      if (messages.size() > 8 && position == 0 && null != mLoadMoreCallbacks && !loadingDataInProgress){
         loadingDataInProgress = true;
         mLoadMoreCallbacks.onLoadMore();
    }


   ......
   //// other adapter code

}

在活动中:

@Override
public void onLoadMore() {
      // Get data from database and add into arrayList
      chatMessagesAdapter.notifyDataSetChanged();

      chatMessagesAdapter. setLoadingDataInProgress(false);
}

这必须解决您的问题,但我更喜欢loadMore在 Activity 或 Presenter 类中处理设置addOnScrollListenerRecyclerView检查findFirstVisibleItemPositioninLayoutManager是否为 0 然后加载数据。

我已经为分页编写了一个库,可以随意使用或自定义它。

PS:正如其他用户提到的那样,不要使用notifyDataSetChanged,因为这将刷新所有视图,包括您不想刷新的可见视图,而是使用notifyItemRangeInsert,在您的情况下,您必须通知从 0 到从数据库加载的数据的大小。在您的情况下,当您从顶部加载时,notifyDataSetChanged会将滚动位置更改为新加载数据的顶部,因此您必须使用它notifyItemRangeInsert来在您的应用中获得良好的感觉

于 2017-04-18T04:21:02.367 回答
0

您需要对特定范围内的项目进行通知

   @Override
    public void onLoadMore() {
        // Get data from database and add into arrayList
        List<Messages> messegaes=getFromDB();
        chatMessagesAdapter.setMessageItemList(messages);
        // Notify adapter with appropriate notify methods
        int curSize = chatMessagesAdapter.getItemCount();
        chatMessagesAdapter.notifyItemRangeInserted(curSize,messages.size());

    }
于 2017-04-19T07:08:24.750 回答
0

您需要在特定范围内指定项目,如下所示:

       @Override
        public void onLoadMore() {
            // Get data from database and add into arrayList
            List<Messages> messegaes=getFromDB();
            chatMessagesAdapter.setMessageItemList(messages);
            // Notify adapter with appropriate notify methods
            int curSize = chatMessagesAdapter.getItemCount();
            chatMessagesAdapter.notifyItemRangeInserted(curSize,messages.size());

        }
于 2017-04-18T05:47:51.687 回答
0

对于这类事情,我不依赖 onBindViewHolder。一个位置可以多次调用。对于具有加载更多选项的列表,也许您应该在您的 recyclerview 膨胀后使用类似的东西。

    recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            if ((((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstCompletelyVisibleItemPosition() == 0)) {
                if (args.listModel.hasMore && null != mLoadMoreCallback && !loadMoreStarted) {
                    mLoadMoreCallbacks.onLoadMore();
                }
            }
        }
    });

希望能帮助到你。

于 2017-03-30T17:13:29.747 回答
0

我建议你使用for操作notifyItemRangeInserted的方法。您将一组新项目添加到列表中,因此您无需通知整个数据集。RecyclerView.AdapterLoadMore

notifyItemRangeInserted(int positionStart, int itemCount)

通知任何已注册的观察者,从 positionStart 开始的当前反映的 itemCount 项已新插入。

更多信息: https ://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html

于 2017-04-12T07:26:14.170 回答
0

在Github上查看 Firebase Friendlychat 源代码。

它的行为就像你想要的那样,特别是在:

    mFirebaseAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
        @Override
        public void onItemRangeInserted(int positionStart, int itemCount) {
            super.onItemRangeInserted(positionStart, itemCount);
            int friendlyMessageCount = mFirebaseAdapter.getItemCount();
            int lastVisiblePosition = mLinearLayoutManager.findLastCompletelyVisibleItemPosition();
            // 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 >= (friendlyMessageCount - 1) && lastVisiblePosition == (positionStart - 1))) {
                mMessageRecyclerView.scrollToPosition(positionStart);
            }
        }
    });
于 2017-04-18T20:00:21.673 回答