web-dev-qa-db-ja.com

データセットが変更されたかどうかを知るためにRecyclerView.Adapterに記述されたオブザーバーはありますか?

RecyclerViewを_Custom Adapter_で次のように実装しました

次のようなグローバル宣言

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

最初に、空のArrayを含むonCreate()メソッド内にアダプタインスタンスを作成し、それを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);
_

注: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は魅​​力のように機能します。

さて、私の質問は、adapternotifyDataSetChanged()を呼び出したことがないのに、どのようにうまく機能しているかということです。アダプター用に以前に登録されたオブザーバーはありますか? public int getItemCount()で返されたデータセットが変更されたかどうかを誰が観察しますか?

documentationから読んだように

void notifyDataSetChanged()

登録されたオブザーバーに、データセットが変更されたことを通知します。

つまり、observersが登録されていても、notifyDataSetChanged()を使用してnotifyする必要があります。正しい?

私も電話しました

_boolean flag = customRecyclerViewAdapter.hasObservers();
_

登録されているオブザーバーがいるかどうかを知るには? FlagTrueです。

だから誰かが私がこれらのものがどのように正確に機能するかを理解するのを手伝ってくれませんか?

16
Nikhil

RecyclerView setAdapter呼び出しのソースを見ると、メソッドsetAdapterInternal(adapter, false, true);があります。

現在のアダプターを新しいアダプターと交換し、リスナーをトリガーします。

このメソッドは、古いアダプターを新しいアダプターと交換する役割を果たし、内部的にはカスタムData Observerにも登録します。これが、フラグをtrueとして取得する理由です。

4
Gautam

私の質問は、アダプターで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

編集-ArrayListのaddAllメソッドを呼び出している独自のaddAllメソッドがあることがわかりました。これが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);

[〜#〜]出力[〜#〜]-[1、2]

これがあなたの場合に起こっていることです。

2
Aniruddha

_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);
_
1
j2ko

私があなたのコードを見ることができることに基づいて、私はあなたの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);
}
1
happydude