je porte mon adaptateur dans RecyclerView.Adapter
ce que je veux réaliser: lorsque l'utilisateur fait défiler vers le bas vers la fin, je veux commencer à récupérer des données, je veux également ajouter une vue ProgressBar à la fin pour permettre à l'utilisateur de savoir que plus de données arrivent.
la façon dont j'ai implémenté cela dans mon BaseAdapter: sur getView
dans la vue demandée vers la fin, je commencerais à récupérer plus de données, j'appellerais notifyDataSetChanged (pour que la vue ProgressBar s'affiche) et ensuite seulement retourner la vue nécessaire pour getView
.
ce que j'ai essayé de faire dans RecyclerView.Adapter: j'ai essayé de faire la même chose en gros, cette fois dans la méthode onBindViewHolder
,
mais si j'essaie d'appeler notifyItemInserted
à l'intérieur de cette méthode, j'obtiens l'exception suivante:
IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling
ce que j'ai essayé: j'ai remarqué que onBindViewHolder
est appelé depuis onLayoutChildren
depuis LayoutManager
, j'ai essayé de le remplacer et d'appeler notifyItemInserted
après son super
mais j'ai la même exception
comment puis-je atteindre mon objectif?
Handler handler = new Handler();
final Runnable r = new Runnable() {
public void run() {
adapter.notifyDataSetChanged();
}
};
handler.post(r);
Mon exemple de code où j'appelle adapter.notifyDataSetChanged (); de l'activité
Il convient également de noter JavaDoc sur la méthode RecyclerView.isComputingLayout()
qui déclenche cette exception:
/**
* Returns whether RecyclerView is currently computing a layout.
* <p>
* If this method returns true, it means that RecyclerView is in a lockdown state and any
* attempt to update adapter contents will result in an exception because adapter contents
* cannot be changed while RecyclerView is trying to compute the layout.
* <p>
* It is very unlikely that your code will be running during this state as it is
* called by the framework when a layout traversal happens or RecyclerView starts to scroll
* in response to system events (touch, accessibility etc).
* <p>
* This case may happen if you have some custom logic to change adapter contents in
* response to a View callback (e.g. focus change callback) which might be triggered during a
* layout calculation. In these cases, you should just postpone the change using a Handler or a
* similar mechanism.
*
* @return <code>true</code> if RecyclerView is currently computing a layout, <code>false</code>
* otherwise
*/
public boolean isComputingLayout() {
return mLayoutOrScrollCounter > 0;
}
La phrase saillante étant:
Dans ces cas, vous devez simplement reporter la modification à l'aide d'un gestionnaire ou d'un mécanisme similaire.
Dans mon cas, je modifiais le contenu de l'adaptateur à partir d'un autre thread, ce qui provoquait (occasionnellement) une collision. Décaler la mise à jour vers un gestionnaire sur le thread d'interface utilisateur résout naturellement cela.
L'utilisation d'un Handler
pour ajouter des éléments et appeler notify...()
à partir de ce Handler
a résolu le problème pour moi.
soigné et facile façon:
recyclerView.post(new Runnable() {
@Override
public void run() {
adapter.notifyDataSetChanged();
}
});
Explication: Vous utilisez votre instance RecyclerView
et à l'intérieur de la méthode post un nouveau Runnable
a été ajouté à la file d'attente des messages. L'exécutable sera exécuté sur le thread d'interface utilisateur. Il s'agit d'une limite pour Android pour accéder au thread d'interface utilisateur depuis l'arrière-plan (par exemple à l'intérieur d'une méthode qui sera exécutée dans un thread d'arrière-plan).
Essayez ceci
if (!recyclerView.isComputingLayout()) {
recyclerView.notifyDataSetChanged();
}
J'utilise ceci:
Handler handler = new Handler();
private void notifyItemInsertedEx(final int position) {
try {
notifyItemInserted(position);
} catch (Exception ex) {
handler.post(new Runnable(){
public void run(){
notifyItemInserted(position);
}
});
}
}