RecyclerView在聊天屏幕中的notifyDataSetChanged上滚动到顶部

时间:2017-03-30 16:25:04

标签: android android-recyclerview recycler-adapter notifydatasetchanged linearlayoutmanager

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

我的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滚动到顶部。如果滚动到顶部停止,则此问题将得到解决。请帮我弄清楚这个问题的原因。提前谢谢。

10 个答案:

答案 0 :(得分:2)

您可以看到,List在插入新项目时滚动到最顶层的项目是很自然的。那么你朝着正确的方向前进,但我想你忘了添加 setReverseLayout(true)

此处setStackFromEnd(true)只是告诉List从视图底部开始堆叠项目,但与setReverseLayout(true)结合使用时,它将颠倒项目和视图的顺序,以便最新项目始终显示在视图的底部。

你的最终layoutManager似乎是这样的:

        mLayoutManager = new LinearLayoutManager(getActivity());
        mLayoutManager.setReverseLayout(true);
        mLayoutManager.setStackFromEnd(true);
        mRecyclerView.setLayoutManager(mLayoutManager);

答案 1 :(得分:2)

我认为你不应该以这种方式使用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;
}

这对我而言是完美的,并且&#34;完全已加载&#34;如果服务器没有返回更多消息,则停止进行服务器调用。希望它可以帮到你。

答案 2 :(得分:1)

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

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

更新: 出于另一个原因,您顶部的另一个视图正在聚焦,因此在您调用任何通知时它将跳至顶部,因此可以通过在GroupView中添加android:focusableInTouchMode =“ true”来删除所有聚焦。

答案 3 :(得分:0)

我不依赖onBindViewHolder来做这些事情。它可以被多次调用一个位置。对于有更多选项的列表,也许你应该在recyclelerview膨胀之后使用类似的东西。

    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();
                }
            }
        }
    });

希望它有所帮助。

答案 4 :(得分:0)

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

notifyItemRangeInserted(int positionStart, int itemCount)
  

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

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

答案 5 :(得分:0)

请勿致电notifyDataSetChanged()上的RecyclerView。使用notifyItemChanged()notifyItemRangeChanged()notifyItemInserted()等新方法... 如果你使用notifyItemRangeInserted() -

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

答案 6 :(得分: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上设置addOnScrollListener的Activity或Presenter类中处理RecyclerView,并检查findFirstVisibleItemPosition中的LayoutManager是否notifyDataSetChanged为0然后加载数据。

我写了一个library for pagination,随意使用或自定义。

PS:正如其他提及的用户不使用notifyItemRangeInsert,因为这会刷新所有视图,包括您不想刷新的视图,而是使用notifyDataSetChanged,在您的情况下,您必须从0通知从数据库加载数据的大小。 在您从顶部加载的情况下,notifyItemRangeInsert会将滚动位置更改为新加载数据的顶部,因此您必须使用.tab-item-active才能在应用中获得良好感觉

答案 7 :(得分: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());

        }

答案 8 :(得分: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);
            }
        }
    });

答案 9 :(得分: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());

    }