How to visually stay on same scroll position, upon unknown number of RecyclerView data changes (insertion, deletion, updates)?

前端 未结 1 1711
失恋的感觉
失恋的感觉 2020-12-06 03:05

Background

In case your RecyclerView gets new items, it is best to use notifyItemRangeInserted, together with unique, stable id for each item, so that

相关标签:
1条回答
  • 2020-12-06 03:23

    I would solve this problem using the DiffUtil api. DiffUtil is meant to take in a "before" and "after" list (that can be as similar or as different as you want) and will compute for you the various insertions, removals, etc that you would need to notify the adapter of.

    The biggest, and nearly only, challenge in using DiffUtil is in defining your DiffUtil.Callback to use. For your proof-of-concept app, I think things will be quite easy. Please excuse the Java code; I know you posted originally in Kotlin but I'm not nearly as comfortable with Kotlin as I am with Java.

    Here's a callback that I think works with your app:

    private static class MyCallback extends DiffUtil.Callback {
    
        private List<ListItemData> oldItems;
        private List<ListItemData> newItems;
    
        @Override
        public int getOldListSize() {
            return oldItems.size();
        }
    
        @Override
        public int getNewListSize() {
            return newItems.size();
        }
    
        @Override
        public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
            return oldItems.get(oldItemPosition).id == newItems.get(newItemPosition).id;
        }
    
        @Override
        public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
            return oldItems.get(oldItemPosition).data == newItems.get(newItemPosition).data;
        }
    }
    

    And here's how you'd use it in your app (in java/kotlin pseudocode):

    addItemsFromTopButton.setOnClickListener {
        MyCallback callback = new MyCallback();
        callback.oldItems = new ArrayList<>(listItems);
    
        // modify listItems however you want... add, delete, shuffle, etc
    
        callback.newItems = new ArrayList<>(listItems);
        DiffUtil.calculateDiff(callback).dispatchUpdatesTo(adapter);
    }
    

    I made my own little app to test this out: each button press would add 20 items, shuffle the list, and then delete 10 items. Here's what I observed:

    • When the first visible item in the "before" list also existed in the "after" list...
      • When there were enough items after it to fill the screen, it stayed in place.
      • When there were not, the RecyclerView scrolled to the bottom
    • When the first visible item in the "before" list did not also exist int he "after" list, the RecyclerView would try to keep whichever item that did exist in both "before" + "after" and was closest to the first visible position in the "before" list in the same position, following the same rules as above.
    0 讨论(0)
提交回复
热议问题