2016年3月6日日曜日

RecyclerView で IndexOutOfBoundsException が起こる話

注意:以下の現象は support library v23.2.0 では起こりません。

support library v23.1.1 で以下のコードを実行すると落ちます。

java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 0(offset:20).state:20 at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4405) ... public class RecyclerViewGoneActivity extends AppCompatActivity { private TextAdapter textAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_recycler_view_gone); final RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view); recyclerView.setLayoutManager(new LinearLayoutManager(this)); // 1. 初期状態がGONE recyclerView.setVisibility(View.GONE); // 2. setHasFixedSize(true); recyclerView.setHasFixedSize(true); textAdapter = new TextAdapter(); recyclerView.setAdapter(textAdapter); new Handler().postDelayed(new Runnable() { @Override public void run() { ArrayList<String> data = new ArrayList<>(); for (int i = 0; i < 20; i++) { data.add("data : " + i); } // 3. notifyItemRangeInserted(); を呼ぶ textAdapter.addAll(data); // 4. visibility を VISIBLE にする recyclerView.setVisibility(View.VISIBLE); } }, 1000); } private static class TextAdapter extends RecyclerView.Adapter<ViewHolder> { private final ArrayList<String> data = new ArrayList<>(); @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { LayoutInflater inflater = LayoutInflater.from(parent.getContext()); return new ViewHolder(inflater.inflate(ViewHolder.LAYOUT_ID, parent, false)); } @Override public void onBindViewHolder(ViewHolder holder, int position) { holder.textView.setText(data.get(position)); } @Override public int getItemCount() { return data.size(); } public void add(String s) { final int positionStart = getItemCount(); data.add(s); notifyItemInserted(positionStart); } public void addAll(ArrayList<String> d) { final int positionStart = getItemCount(); final int itemCount = d.size(); data.addAll(d); notifyItemRangeInserted(positionStart, itemCount); } public void clear() { final int itemCount = getItemCount(); data.clear(); notifyItemRangeRemoved(0, itemCount); } } private static class ViewHolder extends RecyclerView.ViewHolder { public static final int LAYOUT_ID = android.R.layout.simple_list_item_1; final TextView textView; public ViewHolder(View itemView) { super(itemView); this.textView = (TextView) itemView.findViewById(android.R.id.text1); } } }


落ちなくする方法がいくつかあります。
  • 1. setHasFixedSize(true); を呼ばないようにする(ただしその分パフォーマンスに影響する)
  • 2. あらかじめデータを入れておいて VISIBLE にする前に消す
  • textAdapter = new TextAdapter(); recyclerView.setAdapter(textAdapter); textAdapter.add("dummy"); // これを追加 new Handler().postDelayed(new Runnable() { @Override public void run() { textAdapter.clear(); // これを追加 ArrayList<String> data = new ArrayList<>(); for (int i = 0; i < 20; i++) { data.add("data : " + i); } textAdapter.addAll(data); recyclerView.setVisibility(View.VISIBLE); } }, 1000);
関連


0 件のコメント:

コメントを投稿