Android - notifyItemRangeInserted()未正确更新RecyclerView中的位置

时间:2018-05-03 16:58:31

标签: android android-recyclerview pagination google-cloud-firestore indexoutofboundsexception

我想在RecyclerView列表中添加新项目,并始终将其放在列表的最顶部。但遗憾的是,更新列表时出现奇怪的行为我使用getAdapterPosition()来监控索引/位置是否正在更新。

默认列表:0,1,2,3,4是我的第一个列表

My Pagination规则是每次用户在最底部滚动时都会添加5个项目。

现在我想添加新项目并将其放在列表顶部而不使用太多内存,这就是我根本不想使用notifyDataSetChanged()的原因。 但是在添加新项目之后,这就是结果。

添加新项目时:"这是新项目" > 0,0,1,2,3,4 getAdapterPosition()没有得到更新,而是重复索引,因此每次添加新项目时都会创建两个或更多的零索引?

我对notifyItemRangeInserted()的理解是,当添加新项目时,它将更新列表中的其余项目,从我们传递的第一个参数开始,即" positionStart"并在该positionStart之后立即更新项目的下一个/其余部分。

这是我第一次使用Firestore查询将获得onCreate方法的前5项。

   //Load the first item(s) to display
      //Set a query according to time in milliseconds
      mQuery = mDatabase.collection("Announcements")
              .orderBy("time", Query.Direction.DESCENDING)
              .limit(5);

      //Getting all documents under Announcement collection with query's condition
      annon_listener = mQuery.addSnapshotListener(new EventListener<QuerySnapshot>() {
          @Override
          public void onEvent(final QuerySnapshot documentSnapshots, FirebaseFirestoreException e) {

              //If something went wrong
              if (e != null)
                  Log.w(TAG, "Listen failed.", e);


              //If any post exist put it to model and add it to List to populate the CardView
              //If data exist in the first 5 items then item should be loaded making our 'isFirstListLoaded' variable to be true
              if (!documentSnapshots.isEmpty()){

                  //If first item are loaded then every update post should be on the top not at the bottom
                  //This can only be called once to avoid confusion/duplication getting new item
                  if (isFirstListLoaded){
                      //Get the documents of last item listed in our RecyclerView
                      mLastSeen = documentSnapshots.getDocuments().get(documentSnapshots.size()-1);
                      //Clear the list first to get a latest data
                      announcementList.clear();
                  }



                  //Loop to read each document
                  for (DocumentChange doc : documentSnapshots.getDocumentChanges()){

                      //Only added document will be read
                      switch (doc.getType()){

                          case ADDED:
                              //Call the model to populate it with document
                              AnnouncementModel annonPost = doc.getDocument().toObject(AnnouncementModel.class)
                                      .withId(doc.getDocument().getId());

                              //To retrieve all post data in the same time we need to place this if else over here
                              //So user data and his/her post will be retrieve at the same time
                              //This can only be called once to avoid confusion getting new item(s)
                              if (isFirstListLoaded){
                                      announcementList.add(annonPost);
                                      announcementRecyclerAdapter.notifyDataSetChanged();
                              }


                              if (isJustDelete)
                                  isJustDelete = false;

                                  //If someone just remove a post then do nothing and return the state to false
                                  //This will be called once a user added new item to database and put it to top of the list
                              else if (!isFirstListLoaded && !isJustDelete){
                                  //Before adding new item to the list lets save the previous size of the list as a reference
                                  int prevSize = announcementList.size();

                                  //This will be called only if user added some new post
                                  announcementList.add(0, annonPost);
                                  //Update the Recycler adapter that new data is added
                                  announcementRecyclerAdapter.notifyItemRangeInserted(0, announcementList.size() - prevSize);
                              }

                              //Just checking of where's the data fetched from
                              String source = documentSnapshots.getMetadata().isFromCache() ?
                                      "Local" : "Server";

                              Log.d(TAG, "Data fetched from " + source + "\n" + doc.getDocument().getData());
                              break;
                      }



                  }
                  //After the first item/latest post was loaded set it to 
                 false it means that first items are already fetched
                  isFirstListLoaded = false;
              }

              //If no post exist then display no content TextView
              else if (announcementList.isEmpty()){
                  noContent.setVisibility(View.VISIBLE);
                  annonRecyclerView.setVisibility(View.GONE);
              }

当用户向下滚动并到达底部时,将调用此方法以获取数据库中可用的下五个项目。

      //Load more queries
private void loadMoreList(){
    //Load the next item(s) to display
    //Set a query according to time in milliseconds
    //This time start getting data AFTER the last item(s) loaded
    mQuery = mDatabase.collection("Announcements")
                .orderBy("time", Query.Direction.DESCENDING)
                .startAfter(mLastSeen)
                .limit(5);
    //Getting all documents under Announcement collection with query's condition
    annon_listener = mQuery.addSnapshotListener(new EventListener<QuerySnapshot>() {
            @Override
            public void onEvent(final QuerySnapshot documentSnapshots, FirebaseFirestoreException e) {
                //If something went wrong
                if (e != null)
                    Log.w(TAG, "Listen failed.", e);

                //If no more item(s) to load
                if (documentSnapshots.isEmpty()){
                    isFullyLoaded = true;
                    messenger = Snackbar.make(mPullToRefreshView,"No more item(s) to load.",Snackbar.LENGTH_LONG)
                            .setActionTextColor(Color.WHITE)
                            .setAction("Dismiss", new View.OnClickListener() {
                                @Override
                                public void onClick(View v) {
                                    messenger.dismiss();
                                }
                            });
                    messenger.show();
                }

                else{
                    //If more data exist then update our 'mLastSeen' data
                    //Update the last list shown in our RecyclerView
                    mLastSeen = documentSnapshots.getDocuments().get(documentSnapshots.size()-1);

                    //Loop to read each document
                    for (DocumentChange doc : documentSnapshots.getDocumentChanges()){
                        //Only added document will be read
                        switch (doc.getType()){

                            case ADDED:
                                //Call the model to repopulate it with document
                                AnnouncementModel annonPost = doc.getDocument().toObject(AnnouncementModel.class)
                                        .withId(doc.getDocument().getId());

                                //This if condition is use to avoid rumbling the item position when deleting some item
                                if (isJustDelete)
                                    isJustDelete = false;

                                else if (!isJustDelete && !isFullyLoaded){
                                    int prevSize = announcementList.size();
                                    //Add any new item(s) to the List
                                    announcementList.add(announcementList.size(), annonPost);
                                    //Update the Recycler adapter that new data is added
                                    //This trick performs recycling even though we set nested scroll to false
                                    announcementRecyclerAdapter.notifyItemRangeInserted(announcementList.size(), announcementList.size() - prevSize);
                                }

                                //Just checking of where's the data fetched from
                                String source = documentSnapshots.getMetadata().isFromCache() ?
                                        "Local" : "Server";

                                Log.d(TAG, "Data fetched from " + source + "\n" + doc.getDocument().getData());
                                break;

                            case REMOVED:
                                break;

                            case MODIFIED:
                                break;


                        }
             }
                }
            }
        });

如何在不使用notifyDataSetChange()的情况下解决这个问题,或者甚至可以解决这个问题? 另一件事是当我使用notifyItemRangeInserted(0,list.size());它抛出一个错误:          IndexOutOfBoundsException:检测到不一致。

1 个答案:

答案 0 :(得分:1)

我发现使用notifyItemRangeChange(startPosition,list.size())是更新适配器位置的正确方法,而不使用notifyDataSetChanged()。它很有用,因为它只会更新从给定的startPosition到最后一个项目的项目,而不是刷新整个列表。