Оптимизация прокрутки вложенных в друг друга RecyclerView
Теги: Java, Android, RecyclerView
Заметка основана на статье Ninad MG “Optimizing Nested RecyclerView”.
RecyclerView обеспечивает плавную прокрутку списка путем помещения ViewHolder’ов ушедших за пределы экрана элементов в так называемый пул (Pool). Далее, при появлении новых элементов, RecyclerView не создает их заново, но использует ViewHolder’ы из пула, надувая (inflate) их актуальными данными. Таким образом достигается переиспользованиe ViewHolder’а и повышается производетельность фреймворка.
NB: для более полной картины проблемы производительности в RecyclerView см. раздел “Жизнь и смерть ViewHolder’а” в данной заметке. + раздел “Bonus” в этой заметке.
Итак, иногда, для создания некоторых макетов, нам необходимо вкладывать один RecyclerView в другой. Рассмотрим случай, когда горизонтальные RecyclerView вложены в вертикальный.
Когда вы прокручивавете вложенные горизонтальные списки то прокрутка будет плавной, но если прокручивать вертикальный список - прокрутка может притормаживать. Это происходит от того, что у каждого RecyclerView свой пул ViewHolder’ов. И каждый раз, при прокрутке вертикального списка, горизонтальные будут заново создавать свои ViewHolder’ы.
Мы можем исправить эту ситуацию, разделив между всеми RecyclerView один пул ViewHolder’ов.
Это делается с помощью функции RecyclerView.setRecycledViewPool(RecycledViewPool):
RecyclerView.RecycledViewPool viewPool; | |
public OuterRecyclerViewAdapter(List<Item> items) { | |
//получаем пул для внешнего списка | |
viewPool = new RecyclerView.RecycledViewPool(); | |
} | |
@Override | |
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { | |
//для каждого вложенного списка задаем пул | |
holder.innerRecyclerView.setRecycledViewPool(viewPool); | |
} |
Теперь, когда у всех вложенных RecyclerView один пул ViewHolder’ов, они используют ViewHolder’ы друг друга, и прокрутка становится более плавной.
Но прокрутку можно оптимизировать еще больше.
Во-первых, конкретному типу View, можно задать максимальное количество ViewHolder’ов, которые будут хранится в пуле:
recyclerView.getRecycledViewPool() | |
.setMaxRecycledViews(INT_VIEW_TYPE, INT_MAX_POOL_CAPACITY); |
Таким образом, мы можем подкорректировать (т.е. увеличить) максимальные значения количества в пуле тех ViewHolder’ов, которых в каждый момент на экране больше, учитывая тот факт, что по умолчанию это количество равно пяти.
Во-вторых, начиная с support library 25.1.0 у нас есть возможность указать layoutManager’ам внутренних RecyclerView сколько View нужно подготовить перед появлением на экране:
LinearLayoutManager.setInitialPrefetchItemCount(N); |
где N - это количество видимых View.
Таким образом, если на экране внутренние горизонтальные списки за раз показывают минимум три с половиной View, мы можем написать так:
LinearLayoutManager.setInitialPrefetchItemCount(4); |
NB: По-умолчанию данное N = 2
Это позволяет внутренним RecyclerView создавать свои View на ранней стадии, что повышает производительность при прокрутке внешнего списка.