Wen vamos definir setHasFixedSize(true)
em RecyclerView
que os meios tamanho do reciclador é fixo e não é afetada pelo conteúdo do adaptador. E, neste caso, onLayout
não é chamado de reciclador quando atualizamos os dados do adaptador (mas há uma exceção).
Vamos ao exemplo:
RecyclerView
possui 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 requestLayout
a atualização de crianças.
Porém, se definirmos setHasFixedSize(true)
e atualizarmos os dados de um adaptador via notifyItemChanged
, haverá uma chamada para onChange
o padrão do reciclador RecyclerViewDataObserver
e nenhuma chamada para triggerUpdateProcessor
. Nesse caso, o reciclador onLayout
é chamado sempre que definirmos setHasFixedSize
true
ou 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 RecyclerView
e 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 replaceData
e 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 onLayout
método recycler .