代码之家  ›  专栏  ›  技术社区  ›  user9113430

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

  •  1
  • user9113430  · 技术社区  · 8 年前

    我想在RecyclerView列表中添加新项目,并始终将其放在列表的最顶端。但不幸的是,在更新我使用getAdapterPosition()监视索引/位置是否得到更新的列表时,出现了奇怪的行为。

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

    我的分页规则是,每次用户在最底部滚动时,都会添加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);
                  }
    

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

          //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 回复  |  直到 8 年前
        1
  •  2
  •   user9113430 user9113430    8 年前

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