これは、その仕組みを知っているか、ソースコードを掘り下げようとする人のためのRecyclerViewの内部動作に関する質問です。ソースへの参照によって裏付けられた回答をお願いします。
(より絞り込んだ質問を表示するには、「言い換えれば」までスクロールします)
_notify*
_アクション(たとえば、notifyItemInserted()
)がキューに入れられる方法を理解する必要があります。このリストでバックアップされたアダプターがあると想像してください。
_ArrayList<String> list = Arrays.asList("one", "three", "four");
_
不足している値zero
とtwo
を追加します。
_list.add(1, "two");
// notify the view
adapter.notifyItemInserted(1);
// Seconds later, I go on with zero
list.add(0, "zero");
// notify the view
adapter.notifyItemInserted(0);
_
これはかなり単純明快で、何も言うことはありません。
しかし、2つのアクションが互いに非常に近く、間にレイアウトパスがない場合はどうなりますか?
_list.add(1, "two");
list.add(0, "zero”);
_
私は今何をすべきか?
_adapter.notifyItemInserted(1);
adapter.notifyItemInserted(0);
_
または多分
_adapter.notifyItemInserted(2);
adapter.notifyItemInserted(0);
_
?アダプターの観点からすると、リストは_one, three, four
_から_zero, one, two, three, four
_にすぐに切り替わったため、2番目のオプションの方が合理的です。
_list.add(0, “zero”);
adapter.notifyItemInserted(0);
list.add(2, “two”);
adapter.notifyItemInserted(...)
_
今はどうですか? _1
_または_2
_?リストはすぐに更新されましたが、間にレイアウトパスがなかったと思います。
あなたは主要な問題を得ました、そして私はこれらの状況で私がどのように振る舞うべきか知りたいです。実際のケースでは、複数の非同期タスクがinsert()
メソッドで終了しています。私は彼らの操作をキューに入れることができますが:
リサイクラーを更新するには、4つのアクションを実行する必要があります。
adapter.notify*()
を呼び出しますgetItem*()
およびonBind()
を呼び出す)を実行し、変更をレイアウトします。同時実行性がなく、順番に発生する場合、これを理解するのは簡単です。
_1. => 2. => 3. => 4. => (new update) 1. => 2. => 3. => 4. ...
_
ステップ間で何が起こるか見てみましょう。
この場合、どうなるのか知りたい。私たちはどのように振る舞うべきですか?新しいものを挿入する前に、前の更新のステップ4が行われたことを確認する必要がありますか?もしそうなら?
私は以前に同様の質問について考えました、そして私は決めました:
リストの最後に複数のアイテムを直接挿入し、すべてのアニメーションを取得したい場合は、次のようにします。
list.add("0");
list.add("1");
adapter.notifyItemRangeInserted(5, 2); // Suppose there were 5 items before so "0" has index of 5 and we want to insert 2 items.
複数のアイテムをリストの最後に直接挿入したいが、挿入したアイテムごとに個別のアニメーションを取得したい場合は、次のようにします。
list.add("0");
list.add("1");
adapter.notifyItemInserted(0);
mRecyclerView.postDelayed(new Runnable() {
@Override
public void run() {
// before this happens, Be careful to call other notify* methods. Never call notifyDataSetChanged.
adapter.notifyItemInserted(1);
}
}, mRecyclerView.getItemAnimator().getAddDuration());
これがお役に立てば幸いです。
それでは、RetrokerViewが通知アイテムで動作するように、少しイントロから始めましょう。また、保存されたViewGroup項目の他のリスト(ListView for ex。)
RecyclerViewには、すでに描画されているビュー項目のキューがあります。そして、notify(...)
メソッドを呼び出さない限り、更新については何も知りません。新しいアイテムを追加してRecyclerViewに通知すると、すべてのビューを1つずつチェックするサイクルが開始されます。
_RecyclerView contains and drawn next objects
View view-0 (position 0), view-1 (position 1), View-2 (position 2)
// Here is changes after updating
You added Item View view-new into (position 1) and Notify
RecyclerView starts loop to check changes
RecyclerView received unmodified view-0(position-0) and left them;
RecyclerView found new item view-new(position 1)
RecyclerView removing old item view-1(position 1)
RecyclerView drawing new item view-new(position 1)
// In RecyclerView queue in position-2 was item view-2,
// But now we replacing previous item to this position
RecyclerView found new item view-1 (new position-2)
RecyclerView removing old item view-2(position 2)
RecyclerView drawing new item view-1(position 2)
// And again same behavior
RecyclerView found new item view-3 (new position-3)
RecyclerView drawing new item view-1(position 2)
// And after all changes new RecyclerView would be
RecyclerView contains and drawn next objects
View view-0 (position 0), view-new (position 1) view-1 (position 2), View-2 (position 3)
_
これは動作する通知関数のメインフローにすぎませんが、このすべてのアクションがUIスレッド、メインスレッドで発生することを知っておく必要があります。非同期タスクから更新を呼び出すこともできます。そして、2つの質問に答えます-NotifyをRecyclerViewに好きなだけ呼び出すことができ、アクションが正しいキューで行われることを確認してください。
RecyclerViewはどのような使用法でも正しく機能します。より複雑な質問は、アダプターの作業です。まず、アイテムの削除などのアダプタアクションを同期し、インデックスの使用を完全に拒否する必要があります。たとえば、例3の方が良いでしょう。
_Item firstItem = new Item(0, “zero”);
list.add(firstItem);
adapter.notifyItemInserted(list.indexOf(firstItem));
//Other action...
Item nextItem = new Item(2, “two”);
list.add(nextItem);
adapter.notifyItemInserted(list.indexOf(nextItem))
//Other actions
_
UPDATE |
RecyclerView.Adapter Doc に関連しています。ここでは、notifyDataSetChanged()
と同じ関数を見ることができます。そして、この_RecyclerView.Adapter
_が_Android.database.Observable
_拡張子を持つ子アイテムを呼び出す場所については、詳細 About Observable を参照してください。このオブザーバブルホルダーへのアクセスは、RecyclerViewのビュー要素が使用されるまで同期されます。
サポートライブラリバージョン25.0のRecyclerViewも参照してください。Lines 9934-9988;
レイアウトパス間で複数の更新を行っても問題にはなりません。 RecyclerView
は、このケースを処理(および最適化)するように設計されています。
RecyclerViewは、RecyclerView.AdapterとRecyclerView.LayoutManagerの間に追加レベルの抽象化を導入して、レイアウト計算中にバッチでデータセットの変更を検出できるようにします。 [...] RecyclerViewには、2つのタイプの位置関連メソッドがあります。
- レイアウト位置:最新のレイアウト計算におけるアイテムの位置。これは、LayoutManagerから見た位置です。
- アダプターの位置:アダプター内のアイテムの位置。これは、アダプターから見た位置です。
これら2つの位置は同じです。ただし、adapter.notify *イベントのディスパッチと更新されたレイアウトの計算の間の時間は異なります。
あなたの場合の手順は次のとおりです:
データレイヤーを更新します
あなたはadapter.notify*()
を呼び出します
Recyclerviewは変更を記録します(AdapterHelper.mPendingUpdates
コードを正しく理解している場合)。この変更は ViewHolder.getAdapterPosition() には反映されますが、まだ ViewHolder.getLayoutPosition() には反映されません。
1。、2。、3。シーケンスは任意の数で発生する可能性があります2。が1。の直後に続く限り(およびメインスレッドで発生します)。
(1. => 2. => 3.) ... (1. => 2. => 3.) ... 4.