Wen vamos definir setHasFixedSize(true)em RecyclerViewque os meios tamanho do reciclador é fixo e não é afetada pelo conteúdo do adaptador. E, neste caso, onLayoutnão é chamado de reciclador quando atualizamos os dados do adaptador (mas há uma exceção).
Vamos ao exemplo:
RecyclerViewpossui RecyclerViewDataObserver( encontre a implementação padrão neste arquivo ) com vários métodos, o principal importante é:
void triggerUpdateProcessor() {
if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
} else {
mAdapterUpdateDuringMeasure = true;
requestLayout();
}
}
Este método é chamado se definir setHasFixedSize(true)e atualizar dados de um adaptador via: notifyItemRangeChanged, notifyItemRangeInserted, notifyItemRangeRemoved or notifyItemRangeMoved. Nesse caso, não há chamadas para o reciclador onLayout, mas há chamadas para requestLayouta atualização de crianças.
Porém, se definirmos setHasFixedSize(true)e atualizarmos os dados de um adaptador via notifyItemChanged, haverá uma chamada para onChangeo padrão do reciclador RecyclerViewDataObservere nenhuma chamada para triggerUpdateProcessor. Nesse caso, o reciclador onLayouté chamado sempre que definirmos setHasFixedSize trueou false.
// no calls to triggerUpdateProcessor
@Override
public void onChanged() {
assertNotInLayoutOrScroll(null);
mState.mStructureChanged = true;
processDataSetCompletelyChanged(true);
if (!mAdapterHelper.hasPendingUpdates()) {
requestLayout();
}
}
// calls to triggerUpdateProcessor
@Override
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
assertNotInLayoutOrScroll(null);
if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
triggerUpdateProcessor();
}
}
Como verificar sozinho:
Crie personalizado RecyclerViewe substitua:
override fun requestLayout() {
Log.d("CustomRecycler", "requestLayout is called")
super.requestLayout()
}
override fun invalidate() {
Log.d("CustomRecycler", "invalidate is called")
super.invalidate()
}
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
Log.d("CustomRecycler", "onLayout is called")
super.onLayout(changed, l, t, r, b)
}
Defina o tamanho da recicladora para match_parent(em xml). Tente atualizar os dados do adaptador usando replaceDatae replaceOne com a configuração setHasFixedSize(true)e, em seguida false.
// onLayout is called every time
fun replaceAll(data: List<String>) {
dataSet.clear()
dataSet.addAll(data)
this.notifyDataSetChanged()
}
// onLayout is called only for setHasFixedSize(false)
fun replaceOne(data: List<String>) {
dataSet.removeAt(0)
dataSet.addAll(0, data[0])
this.notifyItemChanged(0)
}
E verifique seu log.
Meu log:
// for replaceAll
D/CustomRecycler: requestLayout is called
D/CustomRecycler: onMeasure is called
D/CustomRecycler: onMeasure is called
D/CustomRecycler: onLayout
D/CustomRecycler: requestLayout is called
D/CustomRecycler: requestLayout is called
D/CustomRecycler: onDraw is called
// for replaceOne
D/CustomRecycler: requestLayout is called
D/CustomRecycler: onDraw is called
D/CustomRecycler: requestLayout is called
D/CustomRecycler: onDraw is called
Resumir:
Se definirmos setHasFixedSize(true)e atualizarmos os dados do adaptador notificando um observador de outra maneira que não a chamada notifyDataSetChanged, você terá algum desempenho, porque não há chamadas para o onLayoutmétodo recycler .