RecyclerViewで一度表示されてから行ごとのViewHolderが切り替わる場合のログを眺める
の続き。
レイアウトの切り替えを伴うRecyclerView
の切り替えがうまく行くとはにわかに信じがたかったので、
タップしたら簡単にレイアウトを切り替えるように前回のコードを少し書き換えてログを見てみました。
まとめ
当たり前のことを確かめました。
- レイアウトが切り替わるように
ViewType
を切り替えていくと、最初のうちは、同じItemのカテゴリを切り替える(=Layoutを切り替える)たびにそのレイアウトに対応したViewHolder
が生成される ViewType
一周分のViewHolder
を生成したら、それ以上ViewType
を切り替えても新しいViewHolder
は生成されない- 上記は、同じ
position
のItem
に対して複数のViewHolder
が生成されているというわけでもなく、Adapter
で扱っている範囲で余剰のViewHolder
を生成している - なので、いくらか余剰の
ViewHolder
が生成されたら、他のposition
のItem
を1つ2つ変化させたくらいではもう生成されない。 ViewHolder
の種類のバランスをわざと崩すと新しいレイアウトに対応したViewHolder
が生成される
おまけ
実装中にLayoutPosition
とAdapterPosition
の違いが気になって、下記のStackOverFlowの質問を見て、へええと思ったのでこちらもメモ。
LayoutPosition
とAdapterPosition
はViewHolder
で使うレイアウトを生成している間はずれる可能性があるが、ほとんどの間同じ値を取るnotifyDataSetChanged
で更新したいレイアウトにまつわる処理が重ければ、AdapterPosition
がRecyclerView#NO_POSITION(-1)
になることもある- 1個ずつのアイテムの挿入、削除に関してはそこまで心配要らない
- 何か不都合があって、
AdapterPosition
が取得できなかった場合は処理をしない、という使い方もある LinearLayoutManager
を使っていて、いままさにユーザーのタップしたItem
のposition
を取得したいという場合は、LayoutPosition
がよさそう
書き換えた部分
public class SampleAdapter extends RecyclerView.Adapter<SampleAdapter.ViewHolder> { private final LayoutInflater inflater; private final List<SampleItem> data; public SampleAdapter(Context context, List<SampleItem> data) { this.inflater = LayoutInflater.from(context); this.data = data; } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { switch (viewType) { case 1: Log.d("onCreateViewHolder", "viewType: 1"); return new Sample1ItemViewHolder(inflater.inflate(Sample1ItemViewHolder.LAYOUT_ID, parent, false)); case 2: Log.d("onCreateViewHolder", "viewType: 2"); return new Sample2ItemViewHolder(inflater.inflate(Sample2ItemViewHolder.LAYOUT_ID, parent, false)); case 3: Log.d("onCreateViewHolder", "viewType: 3"); return new Sample3ItemViewHolder(inflater.inflate(Sample3ItemViewHolder.LAYOUT_ID, parent, false)); } return null; } @Override public void onBindViewHolder(ViewHolder holder, int position) { switch (getItemViewType(position)) { case 1: Log.d("onBindViewHolder", "viewType: 1 position: " + position); ((Sample1ItemViewHolder)holder).onBindItemViewHolder(data.get(position)); break; case 2: Log.d("onBindViewHolder", "viewType: 2 position: " + position); ((Sample2ItemViewHolder)holder).onBindItemViewHolder(data.get(position)); break; case 3: Log.d("onBindViewHolder", "viewType: 3 position: " + position); ((Sample3ItemViewHolder)holder).onBindItemViewHolder(data.get(position)); break; } } /* getItemViewType(), getItemCount() は変更ないし特に変わったことしてないので略 */ public class ViewHolder extends RecyclerView.ViewHolder { public ViewHolder(View itemView) { super(itemView); } public void changeCategory() { SampleItem item = data.get(this.getAdapterPosition()); Log.d("changeCategory", "text: " + item.getText() + " category:" + item.getCategory()); if(item.getCategory() == 3) { item.setCategory(1); } else { item.setCategory(item.getCategory() + 1); } notifyItemChanged(this.getAdapterPosition()); } } public class Sample1ItemViewHolder extends ViewHolder { public static final int LAYOUT_ID = R.layout.sample_1_item_view; public final TextView textView; public Sample1ItemViewHolder(View itemView) { super(itemView); textView = (TextView) itemView.findViewById(R.id.textView1); textView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { changeCategory(); } }); } public void onBindItemViewHolder(SampleItem data) { this.textView.setText(data.getText()); } } /* Sample2ItemViewHolder, Sample2ItemViewHolderも同様なので略 */ }
ログ
// パッケージ名を切り取ったLogcatのログ。 // 表示されているItem分のRecyclerViewを一通り生成し終えた後から // 最初のうちは、同じItemのカテゴリを切り替える(=Layoutを切り替える)たびに // そのレイアウトに対応したViewHolderが生成される 11-03 23:00:35.691 D/changeCategory﹕ text: Data0 category:1 11-03 23:00:35.729 D/onCreateViewHolder﹕ viewType: 2 11-03 23:00:35.762 D/onBindViewHolder﹕ viewType: 2 position: 17 11-03 23:00:35.765 D/onCreateViewHolder﹕ viewType: 2 11-03 23:00:35.770 D/onBindViewHolder﹕ viewType: 2 position: 0 11-03 23:00:37.130 D/changeCategory﹕ text: Data0 category:2 11-03 23:00:37.162 D/onCreateViewHolder﹕ viewType: 3 // ViewType一周分のViewHolderを生成したら、 // それ以上ViewTypeを切り替えても新しいViewHolderは生成されない 11-03 23:00:37.177 D/onBindViewHolder﹕ viewType: 3 position: 0 11-03 23:00:38.116 D/changeCategory﹕ text: Data0 category:3 11-03 23:00:38.139 D/onBindViewHolder﹕ viewType: 1 position: 0 11-03 23:00:39.690 D/changeCategory﹕ text: Data0 category:1 11-03 23:00:39.714 D/onBindViewHolder﹕ viewType: 2 position: 0 11-03 23:00:40.321 D/changeCategory﹕ text: Data0 category:2 11-03 23:00:40.339 D/onBindViewHolder﹕ viewType: 3 position: 0 11-03 23:00:40.891 D/changeCategory﹕ text: Data0 category:3 11-03 23:00:40.910 D/onBindViewHolder﹕ viewType: 1 position: 0 11-03 23:01:00.454 D/changeCategory﹕ text: Data0 category:1 11-03 23:01:00.482 D/onBindViewHolder﹕ viewType: 2 position: 0 11-03 23:01:04.044 D/changeCategory﹕ text: Data0 category:2 11-03 23:01:04.059 D/onBindViewHolder﹕ viewType: 3 position: 0 11-03 23:01:04.429 D/changeCategory﹕ text: Data0 category:3 11-03 23:01:04.432 D/onBindViewHolder﹕ viewType: 1 position: 0 11-03 23:01:06.740 D/changeCategory﹕ text: Data1 category:1 // 上記は、同じpositionのItemに対して複数のViewHolderが生成されているというわけでもなく、 // Adapterで扱っている範囲で余剰のViewHolderを生成している。 // なので、いくらか余剰のViewHolderが生成されたら、 // 他のpositionのItemを1つ2つ変化させたくらいではもう生成されない。 11-03 23:01:06.762 D/onBindViewHolder﹕ viewType: 2 position: 1 11-03 23:01:10.153 D/changeCategory﹕ text: Data1 category:2 11-03 23:01:10.181 D/onBindViewHolder﹕ viewType: 3 position: 1 11-03 23:01:11.010 D/changeCategory﹕ text: Data1 category:3 11-03 23:01:11.030 D/onBindViewHolder﹕ viewType: 1 position: 1 11-03 23:01:13.143 D/changeCategory﹕ text: Data1 category:1 11-03 23:01:13.160 D/onBindViewHolder﹕ viewType: 2 position: 1 11-03 23:01:13.683 D/changeCategory﹕ text: Data1 category:2 11-03 23:01:13.712 D/onBindViewHolder﹕ viewType: 3 position: 1 11-03 23:01:13.920 D/changeCategory﹕ text: Data1 category:3 11-03 23:01:13.932 D/onBindViewHolder﹕ viewType: 1 position: 1 11-03 23:01:18.567 D/changeCategory﹕ text: Data4 category:1 11-03 23:01:18.591 D/onBindViewHolder﹕ viewType: 2 position: 4 11-03 23:01:21.577 D/changeCategory﹕ text: Data4 category:2 11-03 23:01:21.589 D/onBindViewHolder﹕ viewType: 3 position: 4 11-03 23:01:22.351 D/changeCategory﹕ text: Data4 category:3 11-03 23:01:22.380 D/onBindViewHolder﹕ viewType: 1 position: 4 11-03 23:01:25.889 D/changeCategory﹕ text: Data5 category:1 11-03 23:01:25.910 D/onBindViewHolder﹕ viewType: 2 position: 5 11-03 23:01:26.623 D/changeCategory﹕ text: Data5 category:2 11-03 23:01:26.642 D/onBindViewHolder﹕ viewType: 3 position: 5 11-03 23:01:27.601 D/changeCategory﹕ text: Data6 category:1 11-03 23:01:27.631 D/onBindViewHolder﹕ viewType: 2 position: 6 // ViewHolderの種類のバランスをわざと崩すと新しいレイアウトに // 対応したViewHolderが生成される 11-03 23:01:29.853 D/changeCategory﹕ text: Data9 category:1 11-03 23:01:29.881 D/onCreateViewHolder﹕ viewType: 2 11-03 23:01:29.899 D/onBindViewHolder﹕ viewType: 2 position: 9 11-03 23:01:33.065 D/changeCategory﹕ text: Data10 category:1 11-03 23:01:33.091 D/onCreateViewHolder﹕ viewType: 2 11-03 23:01:33.091 D/onBindViewHolder﹕ viewType: 2 position: 10 11-03 23:01:33.721 D/changeCategory﹕ text: Data11 category:1 11-03 23:01:33.742 D/onCreateViewHolder﹕ viewType: 2 11-03 23:01:33.742 D/onBindViewHolder﹕ viewType: 2 position: 11