web-dev-qa-db-fra.com

Comment savoir quand RecyclerView a fini de déposer les éléments?

J'ai une RecyclerView qui est dans une CardView. La CardView a une hauteur de 500dp, mais je veux la raccourcir si la RecyclerView est plus petite . Je me demande donc si un auditeur est appelé lorsque la RecyclerView a fini de déposer ses éléments pour la première fois, ce qui rend il est possible de régler la hauteur de la RecyclerView à la hauteur de la CardView (si elle est inférieure à 500dp).

39
wm1sr

J'avais également besoin d'exécuter du code une fois que ma vue du recycleur avait fini de gonfler tous les éléments. J'ai essayé de vérifier onBindViewHolder dans mon adaptateur si la position était la dernière, puis j'ai averti l'observateur. Mais à ce stade, la vue recycleur n'était toujours pas complètement renseignée.

Comme RecyclerView implements ViewGroup, cette réponse était très utile. Vous devez simplement ajouter un OnGlobalLayoutListener à recyclerView:

    View recyclerView = findViewById(R.id.myView);
    recyclerView.getViewTreeObserver()
                    .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                        @Override
                        public void onGlobalLayout() {
                            //At this point the layout is complete and the
                            //dimensions of recyclerView and any child views are known.
                            //Remove listener after changed RecyclerView's height to prevent infinite loop
                            recyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                        }
                    });
44
andrino

Modification de travail de @andrino anwser.

Comme @Juancho l'a souligné dans le commentaire ci-dessus. Cette méthode est appelée plusieurs fois. Dans ce cas, nous souhaitons qu’il ne soit déclenché qu’une fois.

Créer un écouteur personnalisé avec une instance, par exemple

private RecyclerViewReadyCallback recyclerViewReadyCallback;

public interface RecyclerViewReadyCallback {
    void onLayoutReady();
}

Puis définissez OnGlobalLayoutListener sur votre RecyclerView

recyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                if (recyclerViewReadyCallback != null) {
                    recyclerViewReadyCallback.onLayoutReady();
                }
                recyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            }
        });

après cela, vous devez uniquement implémenter un programme d'écoute personnalisé avec votre code

recyclerViewReadyCallback = new RecyclerViewReadyCallback() {
             @Override
             public void onLayoutReady() {
                 //
                 //here comes your code that will be executed after all items are laid down
                 //
             }
};
26
Phatee P

Si vous utilisez Kotlin, il existe alors une solution plus compacte… ..échantillon de ici .
Cet écouteur de présentation est généralement utilisé pour effectuer une opération après la mesure d'une vue. Vous devez donc normalement attendre que largeur et hauteur soient supérieures à 0.
... il peut être utilisé par n'importe quel objet qui étend View et peut également accéder à toutes ses fonctions et propriétés spécifiques à partir du listener.

// define 'afterMeasured' layout listener:
inline fun <T: View> T.afterMeasured(crossinline f: T.() -> Unit) {
    viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
        override fun onGlobalLayout() {
            if (measuredWidth > 0 && measuredHeight > 0) {
                viewTreeObserver.removeOnGlobalLayoutListener(this)
                f()
            }
        }
    })
}

// using 'afterMeasured' handler:
recycler.afterMeasured {
    // do the scroll (you can use the RecyclerView functions and properties directly)
    // ...
}    
2
Gregory

J'ai eu du mal à essayer de supprimer OnGlobalLayoutListener une fois que cela s'est déclenché, mais cela jette un IllegalStateException. Puisque ce dont j'ai besoin, c’est de faire défiler mon recyclerView vers le deuxième élément. Ce que j’ai fait, c’était de vérifier si elle avait déjà des enfants et si c’était la première fois que c’était vrai, alors seulement je fais le parchemin:

public class MyActivity extends BaseActivity implements BalanceView {
    ...
    private boolean firstTime = true;
    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...

        ViewTreeObserver vto = myRecyclerView.getViewTreeObserver();
        vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                if (myRecyclerView.getChildCount() > 0 && MyActivity.this.firstTime){
                    MyActivity.this.firstTime = false;
                    scrollToSecondPosition();
                }
            }
        });
    }
    ...
    private void scrollToSecondPosition() {
        // do the scroll
    }
}

HTH quelqu'un!

(Bien sûr, cela a été inspiré par les réponses @andrino et @Phatee)

2
Eliseo Ocampos

Dans les mêmes cas, vous pouvez également utiliser la méthode RecyclerView.post() pour exécuter votre code une fois que les éléments de la liste/de la grille sont apparus. Dans mes cas, c'était assez joli.

1
PavelGP
// Another way

// Get the values
Maybe<List<itemClass>> getItemClass(){
    return /*    */
}

// Create a listener 
void getAll(DisposableMaybeObserver<List<itemClass>> dmo) {
    getItemClass().subscribeOn(Schedulers.computation())
                  .observeOn(AndroidSchedulers.mainThread())
                  .subscribe(dmo);
}

// In the code where you want to track the end of loading in recyclerView:

DisposableMaybeObserver<List<itemClass>> mSubscriber = new DisposableMaybeObserver<List<itemClass>>() {
        @Override
        public void onSuccess(List<itemClass> item_list) {
            adapter.setWords(item_list);
            adapter.notifyDataSetChanged();
            Log.d("RECYCLER", "DONE");
        }

        @Override
        public void onError(Throwable e) {
            Log.d("RECYCLER", "ERROR " + e.getMessage());
        }

        @Override
        public void onComplete() {
            Log.d("RECYCLER", "COMPLETE");
        }
    };

void getAll(mSubscriber);


//and

@Override
public void onDestroy() {
    super.onDestroy();
    mSubscriber.dispose();
    Log.d("RECYCLER","onDestroy");
}
0
deni5n