RecyclerView dans ScrollView ne fonctionne pas

278

J'essaie d'implémenter une disposition qui contient RecyclerView et ScrollView dans la même disposition.

Modèle de mise en page:

<RelativeLayout>
    <ScrollView android:id="@+id/myScrollView">
       <unrelated data>...</unrealated data>

           <android.support.v7.widget.RecyclerView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:id="@+id/my_recycler_view"
            />
    </ScrollView>


</RelativeLayout>

Problèmes: je peux faire défiler jusqu'au dernier élément de ScrollView

Choses que j'ai essayées:

  1. vue de la carte à l'intérieur du ScrollView( ScrollViewcontient maintenant RecyclerView) - peut voir la carte jusqu'àRecyclerView
  2. la pensée initiale était de mettre en œuvre cela en viewGrouputilisant RecyclerViewau lieu de l' ScrollViewun de ses types de vues est le CardViewmais j'ai obtenu exactement les mêmes résultats qu'avec leScrollView
royB
la source
Découvrez cette approche: stackoverflow.com/a/21878703/684582
Alécio Carvalho
15
une solution simple dans beaucoup de ces cas est d'utiliser à la NestedScrollViewplace, car elle gère de nombreux problèmes de défilement
Richard Le Mesurier
Richard vous a donné la réponse en février. Utilisez un NestedScrollViewau lieu d'un ScrollView. C'est exactement pour ça.
Mike M.
2
Ça ne change rien pour moi.
Luke Allison
2
Pour référence future, si quelqu'un rencontre un problème similaire uniquement avec des périphériques de guimauve / nougat (API 23, 24), consultez ma solution sur stackoverflow.com/a/38995399/132121
Hossain Khan

Réponses:

654

utiliser NestedScrollViewau lieu deScrollView

Veuillez consulter le document de référence NestedScrollView pour plus d'informations.

et ajouter recyclerView.setNestedScrollingEnabled(false);à votreRecyclerView

Yang Peiyong
la source
24
Fonctionne avec: android.support.v4.widget.NestedScrollView
Cocorico
7
garder android:layout_height="wrap_content"pour la mise en page gonflée pour ViewHolder
Sarath Babu
4
Dans une mise NestedScrollViewen page complexe traîne pour moi, contrairement au ScrollView. Recherche d'une solution sans utiliserNestedScrollView
Roman Samoylenko
7
Vous pouvez également ajouter android: nestedScrollingEnabled = "false" au XML au lieu de recyclerView.setNestedScrollingEnabled (false);
kostyabakay
2
Cela a fonctionné pour moi, mais gardez à l'esprit que les éléments à l'intérieur de recyclerView ne sont pas recyclés.
Mostafa Arian Nejad
102

Je sais que je suis en retard dans le jeu, mais le problème persiste même après que Google a corrigé le problème. android.support.v7.widget.RecyclerView

Le problème que j'obtiens maintenant est RecyclerViewde layout_height=wrap_contentne pas prendre la hauteur de tous les problèmes d'objets à l'intérieur ScrollViewqui ne se produisent que sur les versions Marshmallow et Nougat + (API 23, 24, 25).
(MISE À JOUR: Remplacement ScrollViewpar des android.support.v4.widget.NestedScrollViewtravaux sur toutes les versions. J'ai en quelque sorte manqué de tester la solution acceptée . Ajoutée dans mon projet github comme démo.)

Après avoir essayé différentes choses, j'ai trouvé une solution de contournement qui résout ce problème.

Voici ma structure de mise en page en bref:

<ScrollView>
  <LinearLayout> (vertical - this is the only child of scrollview)
     <SomeViews>
     <RecyclerView> (layout_height=wrap_content)
     <SomeOtherViews>

La solution consiste à envelopper le RecyclerViewavec RelativeLayout. Ne me demandez pas comment j'ai trouvé cette solution de contournement !!!¯\_(ツ)_/¯

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:descendantFocusability="blocksDescendants">

    <android.support.v7.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</RelativeLayout>

Un exemple complet est disponible sur le projet GitHub - https://github.com/amardeshbd/android-recycler-view-wrap-content

Voici une capture d'écran de démonstration montrant le correctif en action:

Screencast

Hossain Khan
la source
6
Merci l'homme .. son aide beaucoup .. mais le défilement devient difficile après votre solution donc j'ai mis recyclerview.setNestedScrollingEnabled (false); et maintenant son travail comme un charme.
Sagar Chavada
4
Cette méthode recycle-t-elle les vues? si nous avons environ des centaines d'objets à recycler. Ce n'est pas une solution.
androidXP
1
Oui, @androidXP a raison, ce hack n'est pas une solution pour une longue liste. Mon cas d'utilisation était un élément fixe dans une vue de liste qui était inférieure à 10. Et quant à la façon dont j'ai trouvé l'autre solution de contournement, j'essayais des choses au hasard, c'était l'une d'entre elles :-)
Hossain Khan
perfect.solved mon problème dans la guimauve adnroid et supérieure. ;)
Amir Ziarati
2
si j'utilise cette solution, onBindView est appelé pour tous les éléments de la liste, ce qui n'est pas le cas d'utilisation de recyclerview.
thedarkpassenger
54

Bien que la recommandation

vous ne devez jamais mettre une vue déroulante dans une autre vue déroulante

Est un bon conseil, mais si vous définissez une hauteur fixe sur la vue du recycleur, cela devrait fonctionner correctement.

Si vous connaissez la hauteur de la disposition de l'élément adaptateur, vous pouvez simplement calculer la hauteur de RecyclerView.

int viewHeight = adapterItemSize * adapterData.size();
recyclerView.getLayoutParams().height = viewHeight;
Joakim Engstrom
la source
10
Comment obtenir adapterItemSize dans recyclerview une idée?
Krutik
1
Il fonctionne comme un charme! La petite correction devrait être: int viewHeight = adapterItemSize * adapterData.size (); recyclerView.getLayoutParams (). height = viewHeight;
Vaibhav Jani
3
Comment trouver adapterItemSize?
droidster.me
2
@JoakimEngstrom Quelle est la adapterItemSizevariable?
IgorGanapolsky
1
Évitez la vue recycleur à l'intérieur de la vue Défilement, car la vue Défilement donne à son enfant un espace infini. Cela fait que la vue du recycleur ayant wrap_content comme hauteur pour mesurer infiniment dans la direction verticale (jusqu'au dernier élément de la vue du recycleur). Au lieu d'utiliser la vue recycleur dans la vue de défilement, utilisez uniquement la vue recycleur avec différents types d'élément. Avec cette implémentation, les enfants de la vue de défilement se comporteraient comme un type de vue. Gérez ces types de vues dans la vue du recycleur.
abhishesh
28

Dans le cas où la définition d'une hauteur fixe pour le RecyclerView ne fonctionnait pas pour quelqu'un (comme moi), voici ce que j'ai ajouté à la solution à hauteur fixe:

mRecyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
        int action = e.getAction();
        switch (action) {
            case MotionEvent.ACTION_MOVE:
                rv.getParent().requestDisallowInterceptTouchEvent(true);
                break;
        }
        return false;
    }

    @Override
    public void onTouchEvent(RecyclerView rv, MotionEvent e) {

    }

    @Override
    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

    }
});
Clans
la source
1
En effet, cela a bien fonctionné pour moi. Avec cela en place, j'ai pu déplacer la vue de défilement vers le haut et vers le bas, et lorsque je sélectionne la vue de recyclage pour le défilement, elle a priorité sur la vue de défilement. Merci pour l'astuce
PGMacDesign
1
cela a fonctionné pour moi aussi, assurez-vous simplement d'utiliser getParent sur un enfant direct de scrollview
BigBen3216
Cette méthode fonctionne également sur les distributions de cyanogenmod. La solution à hauteur fixe sur cyanogenmod fonctionne, mais seulement si la hauteur fixe est la hauteur absolue de tous les éléments de la liste, ce qui défie le point d'utiliser la vue de recyclage en premier lieu. A voté.
HappyKatz
J'avais également besoin de recyclerView.setNestedScrollingEnabled (false);
Analizer
15

RecyclerViews est bien pour mettre dans ScrollViews tant qu'ils ne défilent pas eux-mêmes. Dans ce cas, il est logique d'en faire une hauteur fixe.

La bonne solution consiste à utiliser wrap_contentsur la hauteur de RecyclerView, puis à implémenter un LinearLayoutManager personnalisé qui peut gérer correctement l'emballage.

Copiez ce LinearLayoutManager dans votre projet: https://github.com/serso/android-linear-layout-manager/blob/master/lib/src/main/java/org/solovyev/android/views/llm/LinearLayoutManager.java

Enveloppez ensuite le RecyclerView:

<android.support.v7.widget.RecyclerView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>

Et configurez-le comme suit:

    RecyclerView list = (RecyclerView)findViewById(R.id.list);
    list.setHasFixedSize(true);
    list.setLayoutManager(new com.example.myapp.LinearLayoutManager(list.getContext()));
    list.setAdapter(new MyViewAdapter(data));

Modifier: cela peut entraîner des complications avec le défilement, car le RecyclerView peut voler les événements tactiles du ScrollView. Ma solution consistait simplement à abandonner le RecyclerView dans son ensemble et à utiliser un LinearLayout, à gonfler par programme des sous-vues et à les ajouter à la mise en page.

jcady
la source
4
Impossible d'appeler setNestedScrollingEnabled (false) sur la vue de recyclage?
Eshaan
14

Pour ScrollView, vous pouvez utiliser fillViewport=trueet créer layout_height="match_parent"comme ci-dessous et mettre la vue du recycleur à l'intérieur:

<ScrollView
    android:fillViewport="true"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_below="@+id/llOptions">
          <android.support.v7.widget.RecyclerView
            android:id="@+id/rvList"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            />
</ScrollView>

Aucun autre réglage de la hauteur n'est nécessaire grâce au code.

mustaq
la source
Testé fonctionne correctement avec la version 23.2.1. L'utilisais pour ajouter une disposition au-dessus de recyclerview.
winsontan520
juste le défilement RecyclerView et le défilement ScrollView
Samad
13

Calculer RecyclerViewla hauteur manuellement n'est pas bon, il vaut mieux utiliser une personnalisation LayoutManager.

La raison en question ci - dessus est une vue qui a défilement de ce ( ListView, GridView, RecyclerView) n'a pas réussi à calculer sa hauteur quand ajouter comme un enfant dans un autre point de vue a défilement. Donc, remplacer sa onMeasureméthode résoudra le problème.

Veuillez remplacer le gestionnaire de mise en page par défaut par le suivant:

public class MyLinearLayoutManager extends android.support.v7.widget.LinearLayoutManager {

private static boolean canMakeInsetsDirty = true;
private static Field insetsDirtyField = null;

private static final int CHILD_WIDTH = 0;
private static final int CHILD_HEIGHT = 1;
private static final int DEFAULT_CHILD_SIZE = 100;

private final int[] childDimensions = new int[2];
private final RecyclerView view;

private int childSize = DEFAULT_CHILD_SIZE;
private boolean hasChildSize;
private int overScrollMode = ViewCompat.OVER_SCROLL_ALWAYS;
private final Rect tmpRect = new Rect();

@SuppressWarnings("UnusedDeclaration")
public MyLinearLayoutManager(Context context) {
    super(context);
    this.view = null;
}

@SuppressWarnings("UnusedDeclaration")
public MyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
    super(context, orientation, reverseLayout);
    this.view = null;
}

@SuppressWarnings("UnusedDeclaration")
public MyLinearLayoutManager(RecyclerView view) {
    super(view.getContext());
    this.view = view;
    this.overScrollMode = ViewCompat.getOverScrollMode(view);
}

@SuppressWarnings("UnusedDeclaration")
public MyLinearLayoutManager(RecyclerView view, int orientation, boolean reverseLayout) {
    super(view.getContext(), orientation, reverseLayout);
    this.view = view;
    this.overScrollMode = ViewCompat.getOverScrollMode(view);
}

public void setOverScrollMode(int overScrollMode) {
    if (overScrollMode < ViewCompat.OVER_SCROLL_ALWAYS || overScrollMode > ViewCompat.OVER_SCROLL_NEVER)
        throw new IllegalArgumentException("Unknown overscroll mode: " + overScrollMode);
    if (this.view == null) throw new IllegalStateException("view == null");
    this.overScrollMode = overScrollMode;
    ViewCompat.setOverScrollMode(view, overScrollMode);
}

public static int makeUnspecifiedSpec() {
    return View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
}

@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);

    final boolean hasWidthSize = widthMode != View.MeasureSpec.UNSPECIFIED;
    final boolean hasHeightSize = heightMode != View.MeasureSpec.UNSPECIFIED;

    final boolean exactWidth = widthMode == View.MeasureSpec.EXACTLY;
    final boolean exactHeight = heightMode == View.MeasureSpec.EXACTLY;

    final int unspecified = makeUnspecifiedSpec();

    if (exactWidth && exactHeight) {
        // in case of exact calculations for both dimensions let's use default "onMeasure" implementation
        super.onMeasure(recycler, state, widthSpec, heightSpec);
        return;
    }

    final boolean vertical = getOrientation() == VERTICAL;

    initChildDimensions(widthSize, heightSize, vertical);

    int width = 0;
    int height = 0;

    // it's possible to get scrap views in recycler which are bound to old (invalid) adapter entities. This
    // happens because their invalidation happens after "onMeasure" method. As a workaround let's clear the
    // recycler now (it should not cause any performance issues while scrolling as "onMeasure" is never
    // called whiles scrolling)
    recycler.clear();

    final int stateItemCount = state.getItemCount();
    final int adapterItemCount = getItemCount();
    // adapter always contains actual data while state might contain old data (f.e. data before the animation is
    // done). As we want to measure the view with actual data we must use data from the adapter and not from  the
    // state
    for (int i = 0; i < adapterItemCount; i++) {
        if (vertical) {
            if (!hasChildSize) {
                if (i < stateItemCount) {
                    // we should not exceed state count, otherwise we'll get IndexOutOfBoundsException. For such items
                    // we will use previously calculated dimensions
                    measureChild(recycler, i, widthSize, unspecified, childDimensions);
                } else {
                    logMeasureWarning(i);
                }
            }
            height += childDimensions[CHILD_HEIGHT];
            if (i == 0) {
                width = childDimensions[CHILD_WIDTH];
            }
            if (hasHeightSize && height >= heightSize) {
                break;
            }
        } else {
            if (!hasChildSize) {
                if (i < stateItemCount) {
                    // we should not exceed state count, otherwise we'll get IndexOutOfBoundsException. For such items
                    // we will use previously calculated dimensions
                    measureChild(recycler, i, unspecified, heightSize, childDimensions);
                } else {
                    logMeasureWarning(i);
                }
            }
            width += childDimensions[CHILD_WIDTH];
            if (i == 0) {
                height = childDimensions[CHILD_HEIGHT];
            }
            if (hasWidthSize && width >= widthSize) {
                break;
            }
        }
    }

    if (exactWidth) {
        width = widthSize;
    } else {
        width += getPaddingLeft() + getPaddingRight();
        if (hasWidthSize) {
            width = Math.min(width, widthSize);
        }
    }

    if (exactHeight) {
        height = heightSize;
    } else {
        height += getPaddingTop() + getPaddingBottom();
        if (hasHeightSize) {
            height = Math.min(height, heightSize);
        }
    }

    setMeasuredDimension(width, height);

    if (view != null && overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS) {
        final boolean fit = (vertical && (!hasHeightSize || height < heightSize))
                || (!vertical && (!hasWidthSize || width < widthSize));

        ViewCompat.setOverScrollMode(view, fit ? ViewCompat.OVER_SCROLL_NEVER : ViewCompat.OVER_SCROLL_ALWAYS);
    }
}

private void logMeasureWarning(int child) {
    if (BuildConfig.DEBUG) {
        Log.w("MyLinearLayoutManager", "Can't measure child #" + child + ", previously used dimensions will be reused." +
                "To remove this message either use #setChildSize() method or don't run RecyclerView animations");
    }
}

private void initChildDimensions(int width, int height, boolean vertical) {
    if (childDimensions[CHILD_WIDTH] != 0 || childDimensions[CHILD_HEIGHT] != 0) {
        // already initialized, skipping
        return;
    }
    if (vertical) {
        childDimensions[CHILD_WIDTH] = width;
        childDimensions[CHILD_HEIGHT] = childSize;
    } else {
        childDimensions[CHILD_WIDTH] = childSize;
        childDimensions[CHILD_HEIGHT] = height;
    }
}

@Override
public void setOrientation(int orientation) {
    // might be called before the constructor of this class is called
    //noinspection ConstantConditions
    if (childDimensions != null) {
        if (getOrientation() != orientation) {
            childDimensions[CHILD_WIDTH] = 0;
            childDimensions[CHILD_HEIGHT] = 0;
        }
    }
    super.setOrientation(orientation);
}

public void clearChildSize() {
    hasChildSize = false;
    setChildSize(DEFAULT_CHILD_SIZE);
}

public void setChildSize(int childSize) {
    hasChildSize = true;
    if (this.childSize != childSize) {
        this.childSize = childSize;
        requestLayout();
    }
}

private void measureChild(RecyclerView.Recycler recycler, int position, int widthSize, int heightSize, int[] dimensions) {
    final View child;
    try {
        child = recycler.getViewForPosition(position);
    } catch (IndexOutOfBoundsException e) {
        if (BuildConfig.DEBUG) {
            Log.w("MyLinearLayoutManager", "MyLinearLayoutManager doesn't work well with animations. Consider switching them off", e);
        }
        return;
    }

    final RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) child.getLayoutParams();

    final int hPadding = getPaddingLeft() + getPaddingRight();
    final int vPadding = getPaddingTop() + getPaddingBottom();

    final int hMargin = p.leftMargin + p.rightMargin;
    final int vMargin = p.topMargin + p.bottomMargin;

    // we must make insets dirty in order calculateItemDecorationsForChild to work
    makeInsetsDirty(p);
    // this method should be called before any getXxxDecorationXxx() methods
    calculateItemDecorationsForChild(child, tmpRect);

    final int hDecoration = getRightDecorationWidth(child) + getLeftDecorationWidth(child);
    final int vDecoration = getTopDecorationHeight(child) + getBottomDecorationHeight(child);

    final int childWidthSpec = getChildMeasureSpec(widthSize, hPadding + hMargin + hDecoration, p.width, canScrollHorizontally());
    final int childHeightSpec = getChildMeasureSpec(heightSize, vPadding + vMargin + vDecoration, p.height, canScrollVertically());

    child.measure(childWidthSpec, childHeightSpec);

    dimensions[CHILD_WIDTH] = getDecoratedMeasuredWidth(child) + p.leftMargin + p.rightMargin;
    dimensions[CHILD_HEIGHT] = getDecoratedMeasuredHeight(child) + p.bottomMargin + p.topMargin;

    // as view is recycled let's not keep old measured values
    makeInsetsDirty(p);
    recycler.recycleView(child);
}

private static void makeInsetsDirty(RecyclerView.LayoutParams p) {
    if (!canMakeInsetsDirty) {
        return;
    }
    try {
        if (insetsDirtyField == null) {
            insetsDirtyField = RecyclerView.LayoutParams.class.getDeclaredField("mInsetsDirty");
            insetsDirtyField.setAccessible(true);
        }
        insetsDirtyField.set(p, true);
    } catch (NoSuchFieldException e) {
        onMakeInsertDirtyFailed();
    } catch (IllegalAccessException e) {
        onMakeInsertDirtyFailed();
    }
}

private static void onMakeInsertDirtyFailed() {
    canMakeInsetsDirty = false;
    if (BuildConfig.DEBUG) {
        Log.w("MyLinearLayoutManager", "Can't make LayoutParams insets dirty, decorations measurements might be incorrect");
    }
}
}
Arun Antoney
la source
Comment puis-je appeler cette classe lorsque nous définissons les données sur l'adaptateur?
Milan Gajera
11

Essaye ça. Réponse très tardive. Mais sûrement aider quelqu'un à l'avenir.

Définissez votre Scrollview sur NestedScrollView

<android.support.v4.widget.NestedScrollView>
 <android.support.v7.widget.RecyclerView>
</android.support.v7.widget.RecyclerView>
</android.support.v4.widget.NestedScrollView>

Dans votre Recyclerview

recyclerView.setNestedScrollingEnabled(false); 
recyclerView.setHasFixedSize(false);
Ranjith Kumar
la source
8
l'utilisation de RecyclerView dans NestedScrollView appelle onBindView pour chaque élément de la liste même s'il n'est pas visible. Une solution à ce problème?
thedarkpassenger
8

MISE À JOUR: cette réponse est obsolète maintenant car il existe des widgets comme NestedScrollView et RecyclerView qui prennent en charge le défilement imbriqué.

vous ne devriez jamais mettre une vue défilante dans une autre vue défilante!

Je vous suggère de créer votre vue de recycleur de mise en page principale et de placer vos vues en tant qu'éléments de vue de recycleur.

jetez un oeil à cet exemple, il montre comment utiliser plusieurs vues à l'intérieur de l'adaptateur de vue recycleur. lien vers l'exemple

EC84B4
la source
J'ai une page avec plus d'un recycleur , existe-t-il un autre moyen de le persuader? quelque chose comme instagram ou google play partie commentaire qui charge plus d'enregistrement lorsque vous cliquez sur plus de commentaires
Ashkan
en faire un seul recycleurVoir et mettre vos vues en tant qu'éléments pour ce recycleur
EC84B4
6
C'est absurde. Vous pouvez très bien imbriquer RecyclerViews.
IgorGanapolsky
Cette réponse est désormais obsolète. Nous avons des choses comme NestedScrollView qui permettent des vues déroulantes imbriquées. Les RecyclerViews imbriqués fonctionnent également maintenant.
Corey Horn
7

Si vous mettez à l' RecyclerViewintérieur NestedScrollViewet activez recyclerView.setNestedScrollingEnabled(false);, le défilement fonctionnera bien .
Cependant, il y a un problème

RecyclerView ne recycle pas

Par exemple, vos RecyclerView(à l'intérieur NestedScrollViewou ScrollView) ont 100 articles.
Lors du Activitylancement, 100 éléments seront créés ( onCreateViewHolderet onBindViewHolder100 éléments seront appelés en même temps).
Par exemple, pour chaque élément, vous allez charger une grande image à partir de l'API => activité créée -> 100 images seront chargées.
Cela ralentit et ralentit le démarrage de l'activité.
Solution possible :
- Penser à utiliser RecyclerViewavec plusieurs types.

Cependant, si dans votre cas, il n'y a que quelques éléments RecyclerViewet le recyclage ou le recyclage n'affectent pas beaucoup les performances, vous pouvez l'utiliser à l' RecyclerViewintérieur ScrollViewpour de simples

Phan Van Linh
la source
Montrer comment puis-je faire recycler RecyclerView même dans ScollView? Merci!
Liar
2
@Liar, il n'y a actuellement aucun moyen de faire RecyclerViewrecycler après l'avoir mis ScrollView. Si vous voulez recycler, penser à une autre approche (comme utiliser RecyclerView avec plusieurs types)
Phan Van Linh
4

Vous pouvez utiliser cette méthode soit:

Ajoutez cette ligne à votre vue xml recyclerView:

        android:nestedScrollingEnabled="false"

essayez-le, recyclerview défilera en douceur avec une hauteur flexible

j'espère que cela a aidé.

iDeveloper
la source
2

Il semble que NestedScrollViewcela résout le problème. J'ai testé en utilisant cette disposition:

<android.support.v4.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
>

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/dummy_text"
        />

    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="16dp"
        android:layout_marginRight="16dp"
        >
        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>

    </android.support.v7.widget.CardView>

</LinearLayout>

Et cela fonctionne sans problèmes

royB
la source
Bro, ont toujours le même problème après avoir changé en NestedScrollview à partir de Scrollview.
MohanRaj S
mmm ... pouvez-vous partager du code ... Je n'ai aucun problème mais vous ne pouvez jamais savoir avec ce genre de problèmes
royB
comment puis-je partager mon code par e-mail ou via un débordement de pile
MohanRaj S
2
l'utilisation de ce code appelle onBindView pour tous les éléments de la liste même si ces éléments ne sont pas visibles dans la liste. Cela va à l'encontre de l'objectif du recyclage.
thedarkpassenger
2

J'ai utilisé CustomLayoutManager pour désactiver le défilement de RecyclerView. N'utilisez pas non plus Recycler View comme WrapContent, utilisez-le comme 0dp, Weight = 1

public class CustomLayoutManager extends LinearLayoutManager {
    private boolean isScrollEnabled;

    // orientation should be LinearLayoutManager.VERTICAL or HORIZONTAL
    public CustomLayoutManager(Context context, int orientation, boolean isScrollEnabled) {
        super(context, orientation, false);
        this.isScrollEnabled = isScrollEnabled;
    }

    @Override
    public boolean canScrollVertically() {
        //Similarly you can customize "canScrollHorizontally()" for managing horizontal scroll
        return isScrollEnabled && super.canScrollVertically();
    }
}

Utilisez CustomLayoutManager dans RecyclerView:

CustomLayoutManager mLayoutManager = new CustomLayoutManager(getBaseActivity(), CustomLayoutManager.VERTICAL, false);
        recyclerView.setLayoutManager(mLayoutManager);
        ((DefaultItemAnimator) recyclerView.getItemAnimator()).setSupportsChangeAnimations(false); 
        recyclerView.setAdapter(statsAdapter);

UI XML:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/background_main"
    android:fillViewport="false">


    <LinearLayout
        android:id="@+id/contParentLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <edu.aku.family_hifazat.libraries.mpchart.charts.PieChart
                android:id="@+id/chart1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="@dimen/x20dp"
                android:minHeight="@dimen/x300dp">

            </edu.aku.family_hifazat.libraries.mpchart.charts.PieChart>


        </FrameLayout>

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1">


        </android.support.v7.widget.RecyclerView>


    </LinearLayout>


</ScrollView>
Hamza Khan
la source
2

J'avais le même problème. C'est ce que j'ai essayé et ça marche. Je partage mon code xml et java. J'espère que cela aidera quelqu'un.

Voici le xml

<?xml version="1.0" encoding="utf-8"?>

< NestedScrollView
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ImageView
            android:id="@+id/iv_thumbnail"
            android:layout_width="match_parent"
            android:layout_height="200dp" />

        <TextView
            android:id="@+id/tv_description"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Description" />

        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Buy" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Reviews" />
        <android.support.v7.widget.RecyclerView
            android:id="@+id/rc_reviews"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

        </android.support.v7.widget.RecyclerView>

    </LinearLayout>
</NestedScrollView >

Voici le code java associé. Il fonctionne comme un charme.

LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setNestedScrollingEnabled(false);
Zeeshan Shabbir
la source
Qu'en est-il des dispositions ci-dessous RecyclerView?
Tapa Save
ça marche aussi. Vous avez juste besoin d'utiliser. NestedScrollViewau lieu de la vue de défilement
Zeeshan Shabbir
Oui, uniquement NestedScrollView. Mais la solution avec NestedScrollView + RecycleView appelle des populations très lentes de RecyclerView (je pense que pour le calcul de la position Y de la première vue après RecyclerView)
Tapa Save
1

Cela fait l'affaire:

recyclerView.setNestedScrollingEnabled(false);
Shylendra Madda
la source
0

En fait, le but principal de la RecyclerViewest de compenser ListViewet ScrollView. Au lieu de faire ce que vous faites réellement: avoir un RecyclerViewdans un ScrollView, je suggérerais de n'en avoir qu'un RecyclerViewqui peut gérer de nombreux types d'enfants.

Samer
la source
1
Cela ne fonctionnerait que si vos enfants peuvent être récupérés dès que vous les faites défiler. Si vous avez des enfants qui sont mapFragments ou streetviews, cela n'a pas de sens car ils sont obligés de recharger chaque fois qu'ils font défiler la vue de recyclage. Les incorporer dans une vue de défilement puis générer une vue de recyclage en bas est alors plus logique.
Simon
1
@Simon, il y a un setIsRecyclable () pratique dans ViewHolder
ernazm
RecyclerView remplace ListView, il n'est pas destiné à remplacer ScrollView.
cyroxis
Ce commentaire mérite mieux. C'est une solution, et encore meilleure que celle acceptée.
Kai Wang
0

Vous devez d'abord utiliser à la NestedScrollViewplace de ScrollViewet mettre l' RecyclerViewintérieur NestedScrollView.

Utilisez la classe de disposition personnalisée pour mesurer la hauteur et la largeur de l'écran:

public class CustomLinearLayoutManager extends LinearLayoutManager {

public CustomLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
    super(context, orientation, reverseLayout);
}

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

@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(i, View.MeasureSpec.UNSPECIFIED),
                    heightSpec,
                    mMeasuredDimension);

            width = width + mMeasuredDimension[0];
            if (i == 0) {
                height = mMeasuredDimension[1];
            }
        } else {
            measureScrapChild(recycler, i,
                    widthSpec,
                    View.MeasureSpec.makeMeasureSpec(i, 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);
    recycler.bindViewToPosition(view, position);
    if (view != null) {
        RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
        int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
                getPaddingLeft() + getPaddingRight(), p.width);
        int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
                getPaddingTop() + getPaddingBottom(), p.height);
        view.measure(childWidthSpec, childHeightSpec);
        measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin;
        measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin;
        recycler.recycleView(view);
    }
}
}

Et implémentez le code ci-dessous dans l'activité / le fragment de RecyclerView:

 final CustomLinearLayoutManager layoutManager = new CustomLinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);

    recyclerView.setLayoutManager(layoutManager);
    recyclerView.setAdapter(mAdapter);

    recyclerView.setNestedScrollingEnabled(false); // Disables scrolling for RecyclerView, CustomLinearLayoutManager used instead of MyLinearLayoutManager
    recyclerView.setHasFixedSize(false);

    recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);

            int visibleItemCount = layoutManager.getChildCount();
            int totalItemCount = layoutManager.getItemCount();
            int lastVisibleItemPos = layoutManager.findLastVisibleItemPosition();
            Log.i("getChildCount", String.valueOf(visibleItemCount));
            Log.i("getItemCount", String.valueOf(totalItemCount));
            Log.i("lastVisibleItemPos", String.valueOf(lastVisibleItemPos));
            if ((visibleItemCount + lastVisibleItemPos) >= totalItemCount) {
                Log.i("LOG", "Last Item Reached!");
            }
        }
    });
Soni Kumar
la source
0

Si RecyclerView n'affiche qu'une seule ligne dans ScrollView. Il vous suffit de définir la hauteur de votre ligne sur android:layout_height="wrap_content".

SANAT
la source
0

vous pouvez également remplacer LinearLayoutManager pour faire rouler la vue d'ensemble du recyclage en douceur

@Override
    public boolean canScrollVertically(){
        return false;
    }
Fred
la source
0

Désolé d'être en retard à la fête, mais il semble qu'il existe une autre solution qui fonctionne parfaitement pour le cas que vous avez mentionné.

Si vous utilisez une vue de recycleur à l'intérieur d'une vue de recycleur, cela semble fonctionner parfaitement bien. Je l'ai personnellement essayé et utilisé, et il ne semble y avoir aucune lenteur ni aucune secousse du tout. Maintenant, je ne sais pas si c'est une bonne pratique ou non, mais l'imbrication de plusieurs vues de recycleur, même la vue de défilement imbriquée, ralentit. Mais cela semble bien fonctionner. Veuillez essayer. Je suis sûr que la nidification sera parfaitement adaptée à cela.

Shailendra Pundhir
la source
0

Une autre approche pour résoudre le problème consiste à utiliser à l' ConstraintLayoutintérieur ScrollView:

<ScrollView>
  <ConstraintLayout> (this is the only child of ScrollView)
    <...Some Views...>
    <RecyclerView> (layout_height=wrap_content)
    <...Some Other Views...>

Mais je m'en tiendrai toujours à l' androidx.core.widget.NestedScrollView approche proposée par Yang Peiyong .

soshial
la source
-2

** Solution qui a fonctionné pour moi
Utilisez NestedScrollView avec la hauteur comme wrap_content

<br> RecyclerView 
                android:layout_width="match_parent"<br>
                android:layout_height="wrap_content"<br>
                android:nestedScrollingEnabled="false"<br>
                  app:layoutManager="android.support.v7.widget.LinearLayoutManager"
                tools:targetApi="lollipop"<br><br> and view holder layout 
 <br> android:layout_width="match_parent"<br>
        android:layout_height="wrap_content"

// Le contenu de votre ligne va ici

maruti060385
la source
Certains commentaires sur une réponse similaire disent que la désactivation du défilement imbriqué va à l'encontre de l'utilisation d'un RecyclerView, c'est-à-dire qu'il ne recyclera pas les vues. Je ne sais pas comment confirmer cela.
jk7
1
La définition de nestedScrollingEnabled = "false" fait que RecyclerView ne recycle PAS ses vues, au moins dans ma configuration (une RecyclerView à l'intérieur d'une NestedScrollView). Confirmé en ajoutant un RecyclerListener à RecyclerView et en définissant un point d'arrêt dans sa méthode onViewRecycled ().
jk7