Есть ли наблюдатели, написанные в 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
.
Так кто угодно, пожалуйста, помогите мне понять, как именно эти вещи работают?
Ответы
Ответ 1
Если вы посмотрите на источник вызова RecyclerView setAdapter, вы найдете метод setAdapterInternal(adapter, false, true);
который отвечает за
Заменяет текущий адаптер новым и запускает прослушиватели.
Этот метод отвечает за замену старого адаптера новым, а внутри он также регистрирует пользовательский Data Observer. Именно по этой причине вы получаете флаг как истинный
Ответ 2
Основываясь на том, что я вижу в вашем коде, я бы сказал, что к вашему 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);
}
Ответ 3
Мой вопрос в том, как он работает очень хорошо, хотя я никогда не вызывал 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 - [один, два]
Это то, что происходит в вашем случае.
Ответ 4
I have shown progress bar and once я fetch data я 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);