Android - notifyItemRangeInserted () не обновляет позицию в RecyclerView должным образом - PullRequest
0 голосов
/ 03 мая 2018

Я хочу добавить новый элемент (ы) в список RecyclerView и всегда размещать его в самом верху списка. Но, к сожалению, странное поведение происходит при обновлении списка, который я использовал getAdapterPosition (), чтобы отслеживать, обновляется ли индекс / позиция.

Список по умолчанию: 0, 1, 2, 3, 4 - мой первый список

Мое правило разбиения на страницы: 5 пунктов будут добавляться каждый раз, когда пользователь прокручивает в самом низу.

Теперь я хочу добавить новый элемент и поместить его в начало списка, не используя слишком много памяти, поэтому я вообще не хочу использовать notifyDataSetChanged (). Но после добавления нового предмета это результат.

При добавлении нового элемента: «это новый элемент»> 0, 0, 1, 2, 3, 4 GetAdapterPosition () не получает обновления, вместо этого он дублирует индекс, создавая индекс из двух или более нулей при каждом добавлении нового элемента?

Насколько я понимаю с notifyItemRangeInserted (), когда добавляется новый элемент, он обновляет оставшиеся элементы в списке, начиная с того, что мы передаем в первом его параметре, который является "positionStart", и обновляет следующий / оставшийся элемент сразу после этой позиции.

Это мой первый запрос с Firestore, который получит первые 5 элементов в методе onCreate.

   //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 Ответ

0 голосов
/ 11 июня 2018

Я обнаружил, что использование notifyItemRangeChange (startPosition, list.size ()) является правильным способом обновления позиции адаптера без использования notifyDataSetChanged (). Это полезно, так как он будет обновлять только элементы с заданной начальной позиции до последнего элемента, а не обновлять весь список.

...