RecyclerView change data set

前端 未结 5 2062
一个人的身影
一个人的身影 2020-12-07 11:33

I want to implement search functionality for my RecyclerView. On text changed i want to change the data that are displayed with this widget. Maybe this question has been ask

5条回答
  •  一向
    一向 (楼主)
    2020-12-07 11:54

    If you have stable ids in your adapter, you can get pretty good results (animations) if you create a new array containing the filtered items and call

    recyclerView.swapAdapter(newAdapter, false);
    

    Using swapAdapter hints RecyclerView that it can re-use view holders. (vs in setAdapter, it has to recycle all views and re-create because it does not know that the new adapter has the same ViewHolder set with the old adapter).

    A better approach would be finding which items are removed and calling notifyItemRemoved(index). Don't forget to actually remove the item. This will let RecyclerView run predictive animations. Assuming you have an Adapter that internally uses an ArrayList, implementation would look like this:

    // adapter code
    final List mItems = new ArrayList(); //contains your items
    public void filterOut(String filter) {
       final int size = mItems.size();
       for(int i = size - 1; i>= 0; i--) {
           if (mItems.get(i).test(filter) == false) {
               mItems.remove(i);
               notifyItemRemoved(i);
           }
       }
    }
    

    It would perform even better if you can batch notifyItemRemoved calls and use notifyItemRangeRemoved instead. It would look sth like: (not tested)

    public void filterOut(String filter) {
       final int size = mItems.size();
       int batchCount = 0; // continuous # of items that are being removed
       for(int i = size - 1; i>= 0; i--) {
           if (mItems.get(i).test(filter) == false) {
               mItems.remove(i);
               batchCount ++;
           } else if (batchCount != 0) { // dispatch batch
               notifyItemRangeRemoved(i + 1, batchCount);
               batchCount = 0;
           }
       }
       // notify for remaining
       if (batchCount != 0) { // dispatch remaining
           notifyItemRangeRemoved(0, batchCount);
       }
    }
    

    You need to extend this code to add items that were previously filtered out but now should be visible (e.g. user deletes the filter query) but I think this one should give the basic idea.

    Keep in mind that, each notify item call affects the ones after it (which is why I'm traversing the list from end to avoid it). Traversing from end also helps ArrayList's remove method performance (less items to shift).

    For example, if you were traversing the list from the beginning and remove the first two items. You should either call

    notifyItemRangeRemoved(0, 2); // 2 items starting from index 0
    

    or if you dispatch them one by one

    notifyItemRemoved(0);
    notifyItemRemoved(0);//because after the previous one is removed, this item is at position 0
    

提交回复
热议问题