Есть ли наблюдатели, написанные в 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);