RecyclerView dentro de ScrollView no funciona


278

Estoy tratando de implementar un diseño que contenga RecyclerView y ScrollView en el mismo diseño.

Plantilla de diseño:

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

Problemas: puedo desplazarme hasta el último elemento de ScrollView

Cosas que probé:

  1. vista de tarjeta dentro de ScrollView(ahora ScrollViewcontiene RecyclerView): puede ver la tarjeta hastaRecyclerView
  2. El pensamiento inicial era implementar esto viewGroupusando en RecyclerViewlugar de ScrollViewdonde una de sus vistas es el tipo CardViewpero obtuve exactamente los mismos resultados que con elScrollView


15
una solución simple en muchos de estos casos es usar NestedScrollViewen su lugar, ya que maneja muchos problemas de desplazamiento
Richard Le Mesurier

Richard te dio la respuesta en febrero. Use a en NestedScrollViewlugar de a ScrollView. Para eso es exactamente.
Mike M.

2
No cambia nada para mí.
Luke Allison

2
Para referencia futura, si alguien está experimentando un problema similar solo con dispositivos de malvavisco / turrón (API 23, 24), consulte mi solución alternativa en stackoverflow.com/a/38995399/132121
Hossain Khan

Respuestas:


654

usar en NestedScrollViewlugar deScrollView

Consulte el documento de referencia NestedScrollView para obtener más información.

y añadir recyclerView.setNestedScrollingEnabled(false); a tuRecyclerView


24
Funciona con: android.support.v4.widget.NestedScrollView
Cocorico

77
mantener android:layout_height="wrap_content"para el diseño inflado para ViewHolder
Sarath Babu

44
En un diseño complejo se NestedScrollViewretrasa para mí, a diferencia del ScrollView. Buscando una solución sin usarNestedScrollView
Roman Samoylenko

77
También puede agregar android: nestedScrollingEnabled = "false" a XML en lugar de recyclerView.setNestedScrollingEnabled (false);
kostyabakay

2
Funcionó para mí, pero tenga en cuenta que los elementos dentro de recyclerView no se reciclan.
Mostafa Arian Nejad

102

Sé que llegué tarde al juego, pero el problema aún existe incluso después de que Google haya solucionado el problema android.support.v7.widget.RecyclerView

La cuestión ahora es consigo RecyclerViewcon layout_height=wrap_contentno tomar la altura de todos los elementos en el interior tema ScrollViewque solamente ocurre en las versiones de malvavisco y Turrón + (API 23, 24, 25).
(ACTUALIZACIÓN: Reemplazar ScrollViewcon android.support.v4.widget.NestedScrollViewtrabajos en todas las versiones. De alguna manera me perdí la prueba de la solución aceptada . Agregué esto en mi proyecto github como demostración).

Después de probar cosas diferentes, he encontrado una solución que soluciona este problema.

Aquí está mi estructura de diseño en pocas palabras:

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

La solución es la envoltura RecyclerViewcon RelativeLayout. ¡No me pregunten cómo encontré esta solución!¯\_(ツ)_/¯

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

El ejemplo completo está disponible en el proyecto GitHub : https://github.com/amardeshbd/android-recycler-view-wrap-content

Aquí hay un screencast de demostración que muestra la solución en acción:

Screencast


66
Gracias hombre ... es de gran ayuda ... pero el desplazamiento se vuelve difícil después de su solución, así que configuré recyclerview.setNestedScrollingEnabled (falso); y ahora funciona como un encanto.
Sagar Chavada

44
¿Este método recicla vistas? si tenemos alrededor de cientos de objetos para reciclar. Este es un truco, no una solución.
androidXP

1
Sí, @androidXP tiene razón, este truco no es una solución para una larga lista. Mi caso de uso fue un elemento fijo en una vista de lista que era inferior a 10. Y en cuanto a cómo encontré la otra solución, estaba probando cosas al azar, este era uno de ellos :-)
Hossain Khan

perfecto. resolvió mi problema en malvavisco adnroid y superior. ;)
Amir Ziarati

2
si uso esta solución, se llama a onBindView para todos los elementos de la lista, que no es el caso de uso de la vista de reciclaje.
thedarkpassenger

54

Aunque la recomendación de que

nunca debe poner una vista desplazable dentro de otra vista desplazable

Es un buen consejo, sin embargo, si establece una altura fija en la vista del reciclador, debería funcionar bien.

Si conoce la altura del diseño del elemento del adaptador, puede calcular la altura de RecyclerView.

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

10
¿Cómo obtener adapterItemSize en recicladorver alguna idea?
Krutik

1
¡Funciona a las mil maravillas! Pequeña corrección debería ser: int viewHeight = adapterItemSize * adapterData.size (); recyclerView.getLayoutParams (). height = viewHeight;
Vaibhav Jani

3
¿Cómo averiguar adapterItemSize?
droidster.me

2
@JoakimEngstrom ¿Cuál es la adapterItemSizevariable?
IgorGanapolsky

1
Evite la vista de reciclador dentro de la vista de desplazamiento, porque la vista de desplazamiento le da a su hijo un espacio infinito. Esto hace que la vista del reciclador que tiene wrap_content como altura se mida infinitamente en dirección vertical (hasta el último elemento de la vista del reciclador). En lugar de usar la vista de reciclador dentro de la vista de desplazamiento, use solo la vista de reciclador con diferentes tipos de elementos. Con esta implementación, los hijos de la vista de desplazamiento se comportarían como un tipo de vista. Maneje esos tipos de vista dentro de la vista de reciclador.
abhishesh

28

En caso de que configurar la altura fija para RecyclerView no funcione para alguien (como yo), esto es lo que he agregado a la solución de altura fija:

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) {

    }
});

1
De hecho, esto funcionó bien para mí. Con esto en su lugar, pude mover la vista de desplazamiento hacia arriba y hacia abajo, y cuando selecciono la vista de reciclador para el desplazamiento, tiene prioridad sobre la vista de desplazamiento. Gracias por el consejo
PGMacDesign

1
también funcionó para mí, solo asegúrese de usar getParent en un hijo directo de la vista de desplazamiento
BigBen3216

Este método también funciona en distribuciones de cyanogenmod. La solución de altura fija en cyanogenmod funciona, pero solo si la altura fija es la altura absoluta de todos los elementos de la lista, lo que desafía el punto de usar la vista del reciclador en primer lugar. Votado
HappyKatz

También necesitaba recyclerView.setNestedScrollingEnabled (false);
Analizador


15

Las RecyclerViews están bien para ponerlas en ScrollViews siempre que no se desplacen por sí mismas. En este caso, tiene sentido hacerlo a una altura fija.

La solución adecuada es usar wrap_content la altura de RecyclerView y luego implementar un LinearLayoutManager personalizado que pueda manejar adecuadamente el ajuste.

Copie este LinearLayoutManager en su proyecto: https://github.com/serso/android-linear-layout-manager/blob/master/lib/src/main/java/org/solovyev/android/views/llm/LinearLayoutManager.java

Luego envuelva el RecyclerView:

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

Y configúrelo así:

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

Editar: Esto puede causar complicaciones con el desplazamiento porque RecyclerView puede robar los eventos táctiles de ScrollView. Mi solución fue simplemente deshacerme de RecyclerView en su totalidad e ir con un LinearLayout, inflar subvistas programáticamente y agregarlos al diseño.


44
¿No podría llamar a setNestedScrollingEnabled (false) en la vista del reciclador?
Eshaan

14

Para ScrollView, puede usar fillViewport=truey hacer layout_height="match_parent"lo siguiente y poner dentro la vista de reciclador:

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

No se necesitan más ajustes de altura a través del código.


Probado funcionando bien usando v23.2.1. Lo estaba usando para agregar el diseño arriba de la vista de reciclaje.
winsontan520

solo el desplazamiento de RecyclerView y el desplazamiento de ScrollView no
Samad

13

Calcular RecyclerViewla altura de forma manual no es bueno, mejor es usar una costumbre LayoutManager.

El motivo de su emisión por encima de cualquier punto de vista es el que tiene su desplazamiento ( ListView, GridView, RecyclerView) no calculó su altura cuando complemento como un niño en otro punto de vista tiene de desplazamiento. Por lo tanto, anular su onMeasuremétodo resolverá el problema.

Reemplace el administrador de diseño predeterminado con el siguiente:

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");
    }
}
}

¿Cómo puedo llamar a esta clase cuando configuramos los datos en el adaptador?
Milan Gajera

11

Prueba esto. Muy tarde respuesta. Pero seguramente ayudaré a cualquiera en el futuro.

Establezca su Scrollview en NestedScrollView

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

En su Recyclerview

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

8
El uso de RecyclerView dentro de NestedScrollView está llamando a onBindView para cada elemento de la lista, incluso si el elemento no está visible. ¿Alguna solución para ese problema?
thedarkpassenger

8

ACTUALIZACIÓN: esta respuesta está desactualizada ahora ya que hay widgets como NestedScrollView y RecyclerView que admiten desplazamiento anidado.

¡nunca debe poner una vista desplazable dentro de otra vista desplazable!

le sugiero que haga su vista de reciclador de diseño principal y ponga sus vistas como elementos de la vista de reciclador.

Eche un vistazo a este ejemplo, que muestra cómo usar varias vistas dentro del adaptador de vista de reciclador. enlace al ejemplo


Tengo una página con más de un Reciclador , ¿hay alguna otra forma de persuadir esto? algo como comentarios de parte de instagram o google play que cargan más registros cuando haces clic en más comentarios
Ashkan

convertirlo en un solo reciclador Ver y poner sus puntos de vista como elementos para ese reciclador
EC84B4

66
Eso no tiene sentido. Puede tener RecyclerViews anidadas bien.
IgorGanapolsky

Esta respuesta ahora está desactualizada. Tenemos cosas como NestedScrollView que permiten vistas desplazables anidadas. RecyclerViews anidados ahora también funcionan.
Corey Horn

7

Si coloca RecyclerViewdentro NestedScrollViewy habilita recyclerView.setNestedScrollingEnabled(false);, el desplazamiento funcionará bien .
Sin embargo, hay un problema.

RecyclerView no reciclar

Por ejemplo, su RecyclerView(dentro NestedScrollViewo ScrollView) tiene 100 elementos.
Cuando se Activityinicie, se crearán 100 elementos (onCreateViewHolder y se onBindViewHolderinvocarán 100 elementos al mismo tiempo).
Ejemplo, para cada elemento, cargará una imagen grande desde API => actividad creada -> se cargará 100 imágenes.
Hace que la actividad de inicio sea lenta y retrasada.
Posible solución :
- Pensando en usar RecyclerViewcon múltiples tipos.

Sin embargo, si en su caso, solo hay unos pocos elementos RecyclerViewy recicla o no recicla no afecta mucho el rendimiento, puede usarlo por RecyclerViewdentroScrollView


¿Mostrar cómo puedo hacer que RecyclerView recicle incluso dentro de ScollView? ¡Gracias!
Mentiroso

2
@Liar, actualmente no hay forma de hacer el RecyclerViewreciclaje después de ponerlo ScrollView. Si desea reciclar, pensando en otro enfoque (como usar RecyclerView con múltiples tipos)
Phan Van Linh

4

Puedes usar de esta manera:

Agregue esta línea a su vista xml de recyclerView:

        android:nestedScrollingEnabled="false"

pruébelo, la vista de reciclaje se desplazará suavemente con una altura flexible

Espero que esto haya ayudado.


2

Parece que eso NestedScrollViewresuelve el problema. He probado usando este diseño:

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

Y funciona sin problemas.


Hermano, todavía tiene el mismo problema después de cambiar a NestedScrollview desde Scrollview.
MohanRaj S

mmm ... ¿Se puede compartir algo de código ... Estoy tener cero problemas, pero u nunca puede saber con este tipo de problemas
royB

¿Cómo puedo compartir mi código por correo electrónico o por desbordamiento de pila
MohanRaj S

2
el uso de este código llama a onBindView para todos los elementos de la lista, incluso si esos elementos no están visibles en la lista. Esto anula el propósito de la vista de reciclaje.
thedarkpassenger

2

Usé CustomLayoutManager para deshabilitar el desplazamiento RecyclerView. Tampoco use Recycler View como WrapContent, úselo como 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();
    }
}

Utilice CustomLayoutManager en 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>

2

Estaba teniendo el mismo problema. Eso es lo que probé y funciona. Estoy compartiendo mi código xml y java. Espero que esto ayude a alguien.

Aquí está el 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 >

Aquí está el código Java relacionado. Funciona a las mil maravillas.

LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setNestedScrollingEnabled(false);

¿Qué pasa con los diseños debajo de RecyclerView?
Tapa Save

eso también funciona. Solo necesitas usar. NestedScrollViewen lugar de vista de desplazamiento
Zeeshan Shabbir

Sí, solo NestedScrollView. Pero la solución con NestedScrollView + RecycleView llama a poblaciones muy lentas de RecyclerView (creo que para calcular la posición Y de la primera vista después de RecyclerView)
Tapa Save


0

En realidad, el objetivo principal de la RecyclerViewes compensar ListViewy ScrollView. En lugar de hacer lo que realmente está haciendo: tener un RecyclerViewen un ScrollView, sugeriría tener solo uno RecyclerViewque pueda manejar muchos tipos de niños.


1
Esto solo funcionaría siempre que sus hijos puedan ser recolectados de basura tan pronto como los desplace de la vista. Si tiene hijos que son mapFragments o streetviews, no tiene sentido, ya que se ven obligados a volver a cargar cada vez que se desplazan fuera de la vista del reciclador. Incrustarlos en una vista de desplazamiento y luego generar una vista de reciclador en la parte inferior tiene más sentido entonces.
Simon

1
@Simon hay un práctico setIsRecyclable () en ViewHolder
ernazm

RecyclerView reemplaza ListView, no está destinado a reemplazar ScrollView.
cyroxis

Este comentario merece algo mejor. Es una solución, e incluso mejor que la aceptada.
Kai Wang

0

Primero debes usar en NestedScrollViewlugar de ScrollViewy poner el RecyclerViewinteriorNestedScrollView .

Use la clase de diseño personalizado para medir el alto y el ancho de la pantalla:

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

E implemente el siguiente código en la actividad / fragmento 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

Si RecyclerView muestra solo una fila dentro de ScrollView. Solo necesita establecer la altura de su fila en android:layout_height="wrap_content".


0

también puede anular LinearLayoutManager para hacer que la vista de reciclado funcione sin problemas

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

0

Lamento llegar tarde a la fiesta, pero parece que hay otra solución que funciona perfectamente para el caso que mencionaste.

Si usa una vista de reciclador dentro de una vista de reciclador, parece funcionar perfectamente bien. Personalmente lo he intentado y lo he usado, y parece que no da lentitud ni desigualdad alguna. Ahora no estoy seguro de si es una buena práctica o no, pero anidar varias vistas de reciclador, incluso la vista de desplazamiento anidada se ralentiza. Pero esto parece funcionar bien. Por favor inténtalo. Estoy seguro de que la anidación va a estar perfectamente bien con esto.


0

Otro enfoque para abordar el problema es usar ConstraintLayoutdentro ScrollView:

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

Pero aún me apegaría al androidx.core.widget.NestedScrollView enfoque, propuesto por Yang Peiyong .


-2

** Solución que funcionó para mí
Use NestedScrollView con height como 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"

// El contenido de tu fila va aquí


Algunos comentarios sobre una respuesta similar dicen que deshabilitar el desplazamiento anidado anula el propósito de usar un RecyclerView, es decir, que no reciclará las vistas. No estoy seguro de cómo confirmar eso.
jk7

1
Establecer nestedScrollingEnabled = "false" hace que RecyclerView NO recicle sus vistas, al menos en mi configuración (un RecyclerView dentro de un NestedScrollView). Confirmado al agregar un RecyclerListener a RecyclerView y establecer un punto de interrupción dentro de su método onViewRecycled ().
jk7
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.