Wen nous avons mis setHasFixedSize(true)sur RecyclerViewce que les moyens de taille recycleur est fixe et n'est pas affectée par le contenu de l' adaptateur. Et dans ce cas, il onLayoutn'est pas appelé sur recycleur lorsque nous mettons à jour les données de l'adaptateur (mais il y a une exception).
Passons à l'exemple:
RecyclerViewa une RecyclerViewDataObserver( trouver l'implemntation par défaut dans ce fichier ) avec plusieurs méthodes, la principale importante est:
void triggerUpdateProcessor() {
if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
} else {
mAdapterUpdateDuringMeasure = true;
requestLayout();
}
}
Cette méthode est appelée si nous fixons setHasFixedSize(true)et mettre à jour les données d'un adaptateur via: notifyItemRangeChanged, notifyItemRangeInserted, notifyItemRangeRemoved or notifyItemRangeMoved. Dans ce cas, il n'y a pas d'appels au recycleur onLayout, mais il y a des appels à requestLayoutpour mettre à jour les enfants.
Mais si nous définissons setHasFixedSize(true)et mettons à jour les données d'un adaptateur via, notifyItemChangedil y a un appel à onChangela valeur par défaut du recycleur RecyclerViewDataObserveret aucun appel à triggerUpdateProcessor. Dans ce cas, le recycleur onLayoutest appelé chaque fois que nous définissons 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();
}
}
Comment vérifier par vous-même:
Créer une personnalisation RecyclerViewet remplacer:
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)
}
Définissez la taille du recycleur sur match_parent(en xml). Essayez de mettre à jour les données de l'adaptateur en utilisant replaceDataet replaceOne avec la configuration setHasFixedSize(true), puis 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)
}
Et vérifiez votre journal.
Mon journal:
// 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
Résumer:
Si nous définissons setHasFixedSize(true)et mettons à jour les données de l'adaptateur en notifiant un observateur d'une autre manière que l'appel notifyDataSetChanged, alors vous avez des performances, car il n'y a pas d'appels à la onLayoutméthode de recyclage .