web-dev-qa-db-fra.com

RecyclerView dans ScrollView ne fonctionne pas

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

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 dans la ScrollView (maintenant ScrollView contient RecyclerView) - permet de voir la carte jusqu'à la RecyclerView
  2. la pensée initiale était d'implémenter cette viewGroup en utilisant RecyclerView au lieu de ScrollView où l'un de ses types de vues est la CardView mais j'ai obtenu exactement les mêmes résultats qu'avec la ScrollView
204
royB

utilisez NestedScrollView au lieu de ScrollView

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

et ajoutez recyclerView.setNestedScrollingEnabled(false); à votre RecyclerView

496
Yang Peiyong

Bien que la recommandation 

vous ne devriez jamais placer une vue défilable dans une autre vue défilable

C'est un bon conseil, mais si vous définissez une hauteur fixe dans la vue du recycleur, cela devrait fonctionner correctement.

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

int viewHeight = adapterItemSize * adapterData.size();
recyclerView.getLayoutParams().height = viewHeight;
51
Joakim Engstrom

Si le réglage de hauteur fixe pour 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) {

    }
});
24
Clans

La nouvelle Android Support Library 23.2 résout ce problème. Vous pouvez maintenant définir wrap_content comme hauteur de votre RecyclerView et fonctionner correctement.

Bibliothèque de support Android 23.2

23
kevings14

RecyclerViews peut très bien être inséré dans ScrollViews tant qu'ils ne défilent pas eux-mêmes. Dans ce cas, il est judicieux d’en faire une hauteur fixe.

La solution appropriée consiste à utiliser wrap_content sur la hauteur de RecyclerView, puis à implémenter un LinearLayoutManager personnalisé capable de gérer correctement le retour à la ligne.

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

Puis enveloppez le RecyclerView:

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

Et le configurer 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 lors du défilement, car RecyclerView peut dérober les événements tactiles de ScrollView. Ma solution consistait simplement à abandonner RecyclerView et à utiliser LinearLayout, à gonfler par programme les sous-vues et à les ajouter à la présentation.

15
jcady

Pour ScrollView, vous pouvez utiliser fillViewport=true et créer layout_height="match_parent" comme ci-dessous et placer 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 ajustement supplémentaire de la hauteur n'est nécessaire via le code.

14
mustaq

Calculer manuellement la hauteur de RecyclerView n'est pas bon, il vaut mieux utiliser une LayoutManager personnalisée. 

La raison du problème ci-dessus est que toute vue ayant son défilement (ListView, GridView, RecyclerView) n'a pas pu calculer sa hauteur lorsque l'ajout d'un enfant dans une autre vue comporte un défilement. Donc, remplacer sa méthode onMeasure résoudra le problème.

Veuillez remplacer le gestionnaire de présentation 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");
    }
}
}
13
Arun Antoney

Essaye ça. Réponse très tardive. Mais aidez certainement 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 recyclage  

recyclerView.setNestedScrollingEnabled(false); 
recyclerView.setHasFixedSize(false);
9
Ranjith Kumar

UPDATE: Cette réponse est obsolète car il existe des widgets tels que NestedScrollView et RecyclerView qui prennent en charge le défilement imbriqué.

vous ne devriez jamais placer une vue défilable dans une autre vue défilable!

je vous suggère de faire votre vue principale de recycleur de disposition et de mettre vos vues en tant qu'éléments de vue de recycleur.

regardez cet exemple, il montre comment utiliser plusieurs vues dans recycler view adapter . lien vers exemple

7
EC84B4

Si vous mettez RecyclerView dans NestedScrollView et activez recyclerView.setNestedScrollingEnabled(false);, le défilement fonctionnera bien fonctionnera bien.
Cependant, il y a un problème

RecyclerViewne pas recycler

Par exemple, votre RecyclerView (dans NestedScrollView ou ScrollView) comporte 100 éléments.
Lorsque Activity lancera, 100 item créera (onCreateViewHolder et onBindViewHolder of 100 item appelé 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 vont se charger.
Cela rend l'activité initiale lente et retardée.
Solution possible:
- Vous envisagez d'utiliser RecyclerView avec plusieurs types. 

Cependant, si dans votre cas, il y a juste quelques éléments dans RecyclerView et recycler ou ne pas recycler n'affectent pas beaucoup les performances, vous pouvez utiliser RecyclerView à l'intérieur de ScrollView pour plus simple.

5
Linh

Vous pouvez utiliser cette méthode soit:

Ajoutez cette ligne à votre vue recyclerView xml:

        Android:nestedScrollingEnabled="false"

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

espérons que cela a aidé.

4
tahaDev

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"?>

 

<ScrollView
    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>
</ScrollView>

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);
2
Zeeshan Shabbir

Il semble que NestedScrollView résolve le problème. J'ai testé en utilisant cette mise en page:

<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

2
royB

Cela fait le tour:

recyclerView.setNestedScrollingEnabled(false);
1
Shylendra Madda

En fait, le but principal de RecyclerView est de compenser ListView et ScrollView. Au lieu de faire ce que vous faites réellement: avoir une RecyclerView dans une ScrollView, je suggérerais de n'avoir qu'une RecyclerView pouvant gérer plusieurs types d'enfants.

0
Samer

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 recycleur dans une vue recycleur, elle semble fonctionner parfaitement. J'ai personnellement essayé et utilisé, et il semble ne donner aucune lenteur et aucune jerkyness du tout. À présent, je ne sais pas si c'est une bonne pratique ou non, mais l'imbrication de plusieurs vues de recycleur, même l'affichage de défilement imbriqué, ralentit. Mais cela semble bien fonctionner. S'il vous plaît essayez-le. Je suis sûr que nidification va parfaitement bien avec ça.

0
Shailendra Pundhir

J'ai utilisé CustomLayoutManager pour désactiver RecyclerView Scrolling . N'utilisez pas non plus Recycler View en tant que WrapContent, utilisez-le en tant que 0dp, Poids = 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>
0
Hamza Khan

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".

0
SANAT

vous pouvez également remplacer LinearLayoutManager pour que le recyclage des rouleaux se fasse en douceur 

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

D'abord, vous devriez utiliser NestedScrollView au lieu de ScrollView et mettre le RecyclerView à l'intérieur de NestedScrollView.

Utilisez la classe de présentation 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!");
            }
        }
    });
0
Soni Kumar