web-dev-qa-db-fra.com

Comment utiliser RecyclerView dans NestedScrollView?

Comment utiliser RecyclerView dans NestedScrollView? RecyclerView le contenu n'est pas visible après avoir défini l'adaptateur.

UPDATEcode de mise en page mis à jour.

<Android.support.v4.widget.NestedScrollView xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:layout_width="match_parent"
Android:layout_height="match_parent">

<LinearLayout
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:orientation="vertical">

    <RelativeLayout
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:padding="@dimen/keyline_1">

    </RelativeLayout>

    <View
        Android:id="@+id/separator"
        Android:layout_width="match_parent"
        Android:layout_height="1dp"
        Android:background="#e5e5e5" />

    <Android.support.v7.widget.RecyclerView
        Android:id="@+id/conversation"
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content" />

</LinearLayout>

</Android.support.v4.widget.NestedScrollView>
166
Mecid

Remplacez votre recyclerView par, 

<Android.support.v7.widget.RecyclerView
    Android:id="@+id/conversation"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content" />

ici, 

app:layout_behavior="@string/appbar_scrolling_view_behavior"

va gérer le reste des choses. 

Une dernière chose, pas besoin de mettre votre recyclerView dans NestedScrollView

177
Rahul Upadhyay

UPDATE 1

Depuis Android Support Library 23.2.0, la méthode setAutoMeasureEnabled(true) a été ajoutée pour LayoutManagers. Cela permet à RecyclerView d'envelopper son contenu et fonctionne à merveille.
http://Android-developers.blogspot.ru/2016/02/Android-support-library-232.html

Alors, ajoutez quelque chose comme ceci:

    LayoutManager layoutManager = new LinearLayoutManager(this);
    layoutManager.setAutoMeasureEnabled(true);
    recyclerView.setLayoutManager(layoutManager);
    recyclerView.setNestedScrollingEnabled(false);


UPDATE 2

Puisque 27.1.0 setAutoMeasureEnabled est obsolète, vous devez donc fournir une implémentation personnalisée de LayoutManager avec la méthode substituée isAutoMeasureEnabled()

Mais après de nombreux cas d’utilisation, RecyclerView est fortement déconseillé de l’utiliser en mode wrapping} _, car ce n’est pas son objectif. Essayez de refactoriser toute votre mise en page à l’aide de RecyclerView unique et normal avec plusieurs types d’éléments. Ou utilisez l'approche avec LinearLayout que j'ai décrite ci-dessous en dernier recours


Ancienne réponse (non recommandé)

Vous pouvez utiliser RecyclerView à l'intérieur de NestedScrollView. Tout d'abord, vous devez implémenter votre propre LinearLayoutManager personnalisée, cela permet à votre RecyclerView d'envelopper son contenu. Par exemple:

public class WrappingLinearLayoutManager extends LinearLayoutManager
{

    public WrappingLinearLayoutManager(Context context) {
        super(context);
    }

    private int[] mMeasuredDimension = new int[2];

    @Override
    public boolean canScrollVertically() {
        return false;
    }

    @Override
    public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,
            int widthSpec, int heightSpec) {
        final int widthMode = View.MeasureSpec.getMode(widthSpec);
        final int heightMode = View.MeasureSpec.getMode(heightSpec);

        final int widthSize = View.MeasureSpec.getSize(widthSpec);
        final int heightSize = View.MeasureSpec.getSize(heightSpec);

        int width = 0;
        int height = 0;
        for (int i = 0; i < getItemCount(); i++) {
            if (getOrientation() == HORIZONTAL) {
                measureScrapChild(recycler, i,
                        View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
                        heightSpec,
                        mMeasuredDimension);

                width = width + mMeasuredDimension[0];
                if (i == 0) {
                    height = mMeasuredDimension[1];
                }
            } else {
                measureScrapChild(recycler, i,
                        widthSpec,
                        View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
                        mMeasuredDimension);

                height = height + mMeasuredDimension[1];
                if (i == 0) {
                    width = mMeasuredDimension[0];
                }
            }
        }

        switch (widthMode) {
            case View.MeasureSpec.EXACTLY:
                width = widthSize;
            case View.MeasureSpec.AT_MOST:
            case View.MeasureSpec.UNSPECIFIED:
        }

        switch (heightMode) {
            case View.MeasureSpec.EXACTLY:
                height = heightSize;
            case View.MeasureSpec.AT_MOST:
            case View.MeasureSpec.UNSPECIFIED:
        }

        setMeasuredDimension(width, height);
    }

    private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
            int heightSpec, int[] measuredDimension) {

        View view = recycler.getViewForPosition(position);
        if (view.getVisibility() == View.GONE) {
            measuredDimension[0] = 0;
            measuredDimension[1] = 0;
            return;
        }
        // For adding Item Decor Insets to view
        super.measureChildWithMargins(view, 0, 0);
        RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
        int childWidthSpec = ViewGroup.getChildMeasureSpec(
                widthSpec,
                getPaddingLeft() + getPaddingRight() + getDecoratedLeft(view) + getDecoratedRight(view),
                p.width);
        int childHeightSpec = ViewGroup.getChildMeasureSpec(
                heightSpec,
                getPaddingTop() + getPaddingBottom() + getDecoratedTop(view) + getDecoratedBottom(view),
                p.height);
        view.measure(childWidthSpec, childHeightSpec);

        // Get decorated measurements
        measuredDimension[0] = getDecoratedMeasuredWidth(view) + p.leftMargin + p.rightMargin;
        measuredDimension[1] = getDecoratedMeasuredHeight(view) + p.bottomMargin + p.topMargin;
        recycler.recycleView(view);
    }
}

Après cela, utilisez cette LayoutManager pour votre RecyclerView

recyclerView.setLayoutManager(new WrappingLinearLayoutManager(getContext()));

Mais vous devriez aussi appeler ces deux méthodes:

recyclerView.setNestedScrollingEnabled(false);
recyclerView.setHasFixedSize(false);

Ici, setNestedScrollingEnabled(false) désactive le défilement de RecyclerView, de sorte qu'il n'intercepte pas l'événement de défilement de NestedScrollView. Et setHasFixedSize(false) détermine que les modifications apportées au contenu de l'adaptateur peuvent modifier la taille de la variable RecyclerView.

Remarque importante: Cette solution est parfois problématique et pose des problèmes de performances. Par conséquent, si vous avez beaucoup d'éléments dans votre RecyclerView, je vous recommande d'utiliser l'implémentation personnalisée de la vue liste basée sur LinearLayout, créez un analogue de Adaptateur pour lui et le faire se comporter comme ListView ou RecyclerView

110
smbd uknow

1) Vous devez utiliser la bibliothèque de support 23.2.0 (ou) ci-dessus 

2) et RecyclerView height sera wrap_content

3) recyclerView.setNestedScrollingEnabled(false)

Mais en faisant cela, le modèle de recycleur ne fonctionne pas. (c’est-à-dire que toutes les vues seront chargées en même temps, car wrap_content a besoin de la hauteur de RecyclerView complet pour pouvoir dessiner tous les enfants Views à la fois. Aucune vue ne sera recyclée). Essayez de ne pas utiliser ce motif, sauf si c'est vraiment nécessaire. Essayez d'utiliser viewType et ajoutez toutes les autres vues qui doivent défiler jusqu'à RecyclerView plutôt que d'utiliser RecyclerView dans Scrollview. L'impact sur les performances sera très élevé.

Pour simplifier, "cela agit simplement comme LinearLayout avec toutes les vues enfant"

84
Ashok Varma

Vous pouvez utiliser Android:fillViewport="true" pour que NestedScrollView mesure la RecyclerView. La RecyclerView remplira la hauteur restante. donc si vous voulez faire défiler la NestScrollView, vous pouvez définir la RecyclerView 's minHeight.

33
zayn

Ajouter simplement recyclerView.setNestedScrollingEnabled(false); avant setAdapter lui-même a fonctionné pour moi. Je n'ai pas ajouté app:layout_behavior="@string/appbar_scrolling_view_behavior" nulle part et je n'ai défini aucun gestionnaire de présentation personnalisé.

<Android.support.v4.widget.NestedScrollView xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent">

    <LinearLayout
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        Android:background="@color/white"
        Android:orientation="vertical">

        <LinearLayout
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"
            Android:layout_marginLeft="15dp"
            Android:layout_marginRight="15dp"
            Android:orientation="vertical">

            <TextView
                Android:layout_width="match_parent"
                Android:layout_height="wrap_content"
                Android:textColor="@color/white"
                Android:text="Some Text..."
                Android:padding="15dp" />

        </LinearLayout>

        <LinearLayout
            Android:orientation="vertical"
            Android:padding="15dp"
            Android:layout_marginTop="10dp"
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content">

            <TextView
                Android:layout_width="match_parent"
                Android:layout_height="wrap_content"
                Android:text="Quick Links"
                Android:textColor="@color/black"
                Android:textStyle="bold"
                Android:textAllCaps="true"
                Android:paddingLeft="20dp"
                Android:drawableLeft="@drawable/ic_trending_up_black_24dp"
                Android:drawablePadding="10dp"
                Android:layout_marginBottom="10dp"
                Android:textSize="16sp"/>

            <View
                Android:layout_width="fill_parent"
                Android:layout_height="1dp"
                Android:background="#efefef"/>

            <Android.support.v7.widget.RecyclerView xmlns:Android="http://schemas.Android.com/apk/res/Android"
                Android:id="@+id/recyclerview"
                Android:layout_width="match_parent"
                Android:layout_height="match_parent" />
        </LinearLayout>

    </LinearLayout>

</Android.support.v4.widget.NestedScrollView>
25
Vignesh Sundar

C'est ce qui fonctionne pour moi

<Android.support.v4.widget.NestedScrollView
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:fillViewport="true"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

    <Android.support.v7.widget.RecyclerView
        Android:id="@+id/rv_recycler_view"
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</Android.support.v4.widget.NestedScrollView>
19
Ness Tyagi

Essayez d'utiliser cette bibliothèque - https://github.com/serso/Android-linear-layout-manager .

LayoutManager de la bibliothèque permet à RecyclerView d’envelopper son contenu. Dans ce cas, RecyclerView sera "aussi grand que les vues intérieures", il n'aura donc pas de barre de défilement et l'utilisateur utilisera les capacités de défilement de NestedScrollView. Par conséquent, il ne sera pas ambigu comme "scrollable inside scrollable".

7
alcsan

Il y a un code simple et de test que vous pouvez vérifier

<Android.support.v4.widget.NestedScrollView
     Android:layout_width="match_parent"
     Android:layout_height="match_parent"
     Android:fillViewport="true"
     app:layout_behavior="@string/appbar_scrolling_view_behavior">
    <Android.support.v7.widget.RecyclerView
           Android:layout_width="match_parent"
           Android:layout_height="match_parent"/>
   </Android.support.v4.widget.NestedScrollView>
7
Hakim Khan

J'ai utilisé RecyclerView à l'intérieur d'un NestedScrollView et cela a fonctionné pour moi. La seule chose que je devais garder à l'esprit était qu'un NestedScrollView ne prend qu'une seule vue enfant. Ainsi, dans mon cas, j’ai utilisé le groupe de visualisation LienearLayout, qui hébergeait mon RecyclerView, ainsi que plusieurs autres affichages dont j'avais besoin.

Je rencontre un problème en plaçant mon RecyclerView dans le NestedScrollView. J'ai réalisé que le défilement du contenu de mon RecyclerView était relâché. 

J'ai par la suite réalisé que mon RecyclerView recevait l'événement de défilement et était donc en conflit avec le comportement de défilement de NestedScrollView. 

Donc, pour résoudre ce problème, je devais désactiver la fonctionnalité de défilement de mon RecyclerView avec cette méthode movieListNewRecyclerView.setNestedScrollingEnabled(false);

Vous pouvez consulter mon Instagram pour une courte vidéo de ce que j'ai réellement fait. Ceci est mon identifiant Instagram ofelix03

Cliquez sur cette image pour voir ce que j'ai fait

6
Felix Otoo

Voici le code que j'utilise pour éviter les problèmes de défilement:

mRecyclerView = (RecyclerView) view.findViewById(Android.R.id.list);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
mRecyclerView.getLayoutManager().setAutoMeasureEnabled(true);
mRecyclerView.setNestedScrollingEnabled(false);
mRecyclerView.setHasFixedSize(false);
6
Anton Tananaev

J'ai Viewpager et RecyclerView à l'intérieur du NestedScrollView. Après avoir ajouté les lignes ci-dessous

recyclerView.setNestedScrollingEnabled(false);
recyclerView.setHasFixedSize(false);

J'ai résolu le problème du défilement lent et du décalage du défilement.

4
Isham

Si vous utilisez RecyclerView-23.2.1 ou une version ultérieure. La solution suivante fonctionnera parfaitement: 

Dans votre mise en page, ajoutez RecyclerView comme ceci:

<Android.support.v7.widget.RecyclerView
        Android:id="@+id/review_list"
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:scrollbars="vertical" />

Et dans votre fichier Java:

RecyclerView mRecyclerView = (RecyclerView) view.findViewById(R.id.recyclerView);
LinearLayoutManager layoutManager=new LinearLayoutManager(getContext());
layoutManager.setAutoMeasureEnabled(true);
mRecyclerView.setLayoutManager(layoutManager);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setAdapter(new YourListAdapter(getContext()));

Ici, layoutManager.setAutoMeasureEnabled(true); fera l'affaire. 

Consultez ce numéro et ce blog de développeur pour plus d'informations.

3
abhilvare

Vous ne pouvez pas utiliser une vue de recycleur dans une vue de défilement imbriquée. La vue défilante imbriquée n’est pas destinée à contenir d’autres vues défilables, mais c’est parce que c’est un enfant d’une disposition défilante elle-même. J'avais le même problème, mais au final, j'ai déplacé ma vue texte pour qu'elle soit une vue d'ensemble dans la vue recyclée, j'ai fait de la vue recyclée un enfant direct de la présentation du coordinateur et j'ai supprimé la vue de défilement imbriquée. Puis tous mes problèmes ont disparu.

3
Barry Irvine

Il y a beaucoup de bonnes réponses. La clé est que vous devez définir nestedScrollingEnabled sur false. Comme mentionné ci-dessus, vous pouvez le faire en code Java:

mRecyclerView.setNestedScrollingEnabled(false);

Mais vous avez également la possibilité de définir la même propriété en code xml (Android:nestedScrollingEnabled="false"):

 <Android.support.v7.widget.RecyclerView
 Android:id="@+id/recyclerview"
 Android:nestedScrollingEnabled="false"
 Android:layout_width="match_parent"
 Android:layout_height="match_parent" />
1
Alex Misulya

Pour androidx, il s'appelle androidx.core.widget.NestedScrollView - et il fait également défiler les mêmes propriétés avec les propriétés isScrollContainer et measureAllChildren activées:

<!-- Scrolling Content -->
<androidx.core.widget.NestedScrollView
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:isScrollContainer="true"
    Android:measureAllChildren="true"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

    <androidx.recyclerview.widget.RecyclerView
        Android:id="@+id/recyclerview"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        Android:fastScrollEnabled="true"
        Android:scrollbarStyle="insideInset"
        Android:scrollbars="vertical"
        Android:splitMotionEvents="false"
        Android:verticalScrollbarPosition="right"/>

</androidx.core.widget.NestedScrollView>
1
Martin Zeitler

Je devais mettre en œuvre CoordinatorLayout avec le défilement dans la barre d’outils et cela me prenait toute la journée. Je l'ai obtenu en supprimant NestedScrollView. Donc, j'utilise simplement RelativeLayout à la racine.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent">

        <Android.support.v7.widget.RecyclerView
            Android:id="@+id/rv_nearby"
            Android:layout_width="match_parent"
            Android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior" />

</RelativeLayout>
1
Bogdan Ustyak

n'utilisez pas recyclerView dans NestedScrollView. cela peut causer des problèmes en cascade! Je suggère d'utiliser ItemViewTypes dans RecyclerView pour gérer plusieurs types de vues. ajoutez simplement un RecyclerView avec match_parent largeur et hauteur. dans votre recyclerViewAdapter, substituez getItemViewType et utilisez position pour gérer la disposition à gonfler. Après cela, vous pouvez gérer votre détenteur de vue en utilisant la méthode onBindViewHolder.

0
Hossein Karami

Si vous utilisez RecyclerView ScrollListener à l'intérieur de NestedScrollView, l'écouteur addOnScrollListener ne fonctionne pas correctement si vous utilisez les deux.

Utilisez ce code.

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState); 
              ......
        }
    });

ce code fonctionne très bien RecyclerView ScrollListener à l'intérieur NestedScrollView.

merci

0
Rahul

Une solution pour conserver la fonctionnalité de recyclage de Recyclage et éviter que Recyclage ne charge toutes vos données consiste à définir une hauteur fixe dans le Recyclage lui-même. En faisant cela, la vue de recyclage est limitée au chargement que sa hauteur peut montrer à l'utilisateur, recyclant ainsi son élément si jamais vous faites défiler vers le bas/haut.

0
BradlyMan