Есть ли наблюдатели, написанные в RecyclerView.Adapter, чтобы узнать, был ли изменен набор данных?

Я внедрил свой RecyclerView с его Custom Adapter следующим образом

Глобальные декларации следующим образом

 private LinearLayoutManager linearLayoutManager; private int pastVisibleItems, visibleItemCount, totalItemCount; private CustomRecyclerViewAdapter customRecyclerViewAdapter; 

Сначала я создал экземпляр адаптера внутри onCreate() котором внутри него находится пустой Array и установите его в recyclerView

 linearLayoutManager = new LinearLayoutManager(getActivity()); recyclerView.setLayoutManager(linearLayoutManager); DividerItemDecoration dividerItemDecoration = new DividerItemDecoration( Utility.ItemDecorationConst); recyclerView.addItemDecoration(dividerItemDecoration); customRecyclerViewAdapter = new CustomRecyclerViewAdapter(getActivity()); recyclerView.setAdapter(customRecyclerViewAdapter); recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { visibleItemCount = linearLayoutManager.getChildCount(); totalItemCount = linearLayoutManager.getItemCount(); pastVisibleItems = linearLayoutManager.findFirstVisibleItemPosition(); if (loading) { if ((visibleItemCount + pastVisibleItems) >= totalItemCount) { loading = false; customRecyclerViewAdapter.addProgressBarEntry(); controller.getNextPage(PublisherAppContainerFragment.this); } } } }); 

После рендеринга полного представления, когда я получаю данные из AsyncTask для заполнения recyclerView

Я вызываю следующий метод Adapter для заполнения данных

 customRecyclerViewAdapter.addAll(myArray); 

Note: addAll () не является переопределенным методом

Следующий код моего CustomRecyclerViewAdapter

 class CustomRecyclerViewAdapter extends RecyclerView.Adapter<CustomRecyclerViewAdapter.ViewHolder> { ArrayList<MyModel> arrayList = new ArrayList<>(); Context context; public CustomRecyclerViewAdapter(Context context) { this.context = context; } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { ViewHolder viewHolder = null; //inflated some view return viewHolder; } @Override public void onBindViewHolder(ViewHolder holder, int position) { //binded data to holder } @Override public int getItemCount() { return arrayList.size(); } public void addAll(ArrayList myArray) { this.arrayList.addAll(myArray) } public void clear() { arrayList.clear(); } public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { public CardView cardView; public ViewHolder(View view) { super(view); this.cardView = (CardView) view.findViewById(R.id.card_view); this.cardView.setOnClickListener(this); } @Override public void onClick(View view) { //handle operations } } } 

Поэтому всякий раз, когда я получаю данные из AsynTask я вызываю метод addAll() и recyclerView работает как шарм.

Теперь, мой вопрос заключается в том, как он работает очень хорошо, хотя я никогда не вызывал notifyDataSetChanged() на adapter . Есть ли ранее зарегистрированные наблюдатели для адаптера? Кто наблюдает, был ли изменен набор данных, который был возвращен в public int getItemCount() ?

Как я читал из documentation

Void notifyDataSetChanged ()

Сообщите зарегистрированным наблюдателям, что набор данных изменился.

Это означает, что хотя есть некоторые зарегистрированные observers вам необходимо notify их, используя notifyDataSetChanged() . Правильно?

Я также позвонил

 boolean flag = customRecyclerViewAdapter.hasObservers(); 

Знать, зарегистрированы ли какие-либо наблюдатели? Flag True .

Так кто угодно, пожалуйста, помогите мне понять, как именно эти вещи работают?

Если вы посмотрите на источник вызова RecyclerView setAdapter, вы найдете метод setAdapterInternal(adapter, false, true); Который отвечает за

Заменяет текущий адаптер новым и запускает прослушиватели.

Этот метод отвечает за замену старого адаптера новым и внутренне он также регистрирует пользовательский Data Observer . Именно по этой причине вы получаете флаг как истинный

Основываясь на том, что я вижу в вашем коде, я бы сказал, что к вашему RecyclerView нет наблюдателей, которые собирают изменения и сохраняют список обновленным. Скорее всего, вы просто получаете «повезло», как когда вы просматриваете список, менеджер макетов постоянно вызывает getItemCount() на адаптере, чтобы определить, должно ли оно показывать больше предметов. Всякий раз, когда вы вызываете addAll() , вы тихо обновляете счетчик элементов, и просто появляется, что наблюдатели были уведомлены об изменениях.

Это определенно ошибка, и вы, скорее всего, увидите ее эффекты в своей реализации, если бы вы зависели от определенного наблюдателя, чтобы контролировать какой-либо аспект этого списка, или делаете больше, чем просто добавляете новые элементы в нижнюю часть (например, изменяя или вставляя Между существующими пунктами). Правильная реализация, как вы указали, заключается в вызове notifyDataSetChanged() всякий раз, когда список обновляется, или даже лучше быть более конкретным с тем, что изменилось, если вы можете. Например, вы можете использовать:

 public void addAll(ArrayList myArray) { int positionStart = getItemCount() - 1; this.arrayList.addAll(myArray); notifyItemRangeInserted(positionStart, myArray.size()); } public void clear() { int oldSize = getItemCount(); arrayList.clear(); notifyItemRangeRemoved(0, oldSize); } 

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

Это потому, что по умолчанию метод addAll вызывает notifyDataSetChanged() .

 public void addAll(T ... items) { synchronized (mLock) { if (mOriginalValues != null) { Collections.addAll(mOriginalValues, items); } else { Collections.addAll(mObjects, items); } } if (mNotifyOnChange) notifyDataSetChanged(); } 

А также

 public void addAll(@NonNull Collection<? extends T> collection) { synchronized (mLock) { if (mOriginalValues != null) { mOriginalValues.addAll(collection); } else { mObjects.addAll(collection); } } if (mNotifyOnChange) notifyDataSetChanged(); } 

Вот ссылка: https://github.com/android/platform_frameworks_base/blob/master/core/java/android/widget/ArrayAdapter.java

EDIT – я вижу, что у вас есть собственный метод addAll, который вызывает метод addAll из ArrayList. Вот как работает метод addAll –

  private ArrayList<String> ex1 = new ArrayList(); private ArrayList<String> ex2 = new ArrayList(); private ArrayList<String> ex3 = new ArrayList(); ex1.add("one"); ex2.add("two"); ex3.addAll(ex1); ex3.addAll(ex2); System.out.println(ex3); 

OUTPUT – [один, два]

Это то, что происходит в вашем случае.

I have shown progress bar and once I fetch data I hide the progress bar and make recyclerView visible Если в макете или коде вы установите видимость RecyclerView GONE то макета не произойдет, и поэтому Adapter.getItemsCount() не будет вызван. Поэтому, если вы извлекаете данные и заполняете им массив адаптеров, а затем изменяете видимость RecyclerView от GONE до VISIBLE это приведет к обновлению.

Если вы не вызываете notifyDataSetChanged() RecyclerView не будет знать об обновлении. Я думаю, что в вашем коде есть что-то еще, что вызывает обновление RecyclerView . Чтобы прояснить это поведение, давайте использовать некоторый фиктивный адаптер:

 private class DummyViewHolder extends RecyclerView.ViewHolder { public DummyViewHolder (View itemView) { super(itemView); } } private class Adapter extends RecyclerView.Adapter<DummyViewHolder> { private int mDummySize = 5; @Override public DummyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.dummy_view, parent, false); return new DummyViewHolder(view); } @Override public void onBindViewHolder(DummyViewHolder holder, int position) { } void setSize(int size) { this.mDummySize = size; } @Override public int getItemCount() { return mDummySize; } } 

И в onCraete() :

  ViewHolder v = ... final Adapter adapter = .. ... //postpone adapter update (new Handler()).postDelayed(new Runnable() { @Override public void run() { adapter.setSize(10);//and nothing happend only 5 items on screen } }, 5000);