tipos studio recyclerview lista horizontal cardview android android-layout android-scrollview android-recyclerview android-cardview

android - studio - RecyclerView dentro de ScrollView no funciona



recyclerview horizontal (22)

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

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 ScrollView contiene RecyclerView ): puede ver la tarjeta hacia arriba hasta RecyclerView
  2. La idea inicial era implementar este viewGroup usando RecyclerView lugar de ScrollView donde una de sus vistas es CardView pero obtuve exactamente los mismos resultados que con ScrollView

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 intenté y lo usé, 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.


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


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>


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

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


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;


Calcular la altura de RecyclerView manualmente no es bueno, es mejor usar un LayoutManager personalizado.

La razón del problema anterior es que cualquier vista que tenga su desplazamiento ( ListView , GridView , RecyclerView ) no pudo calcular su altura cuando se agrega un elemento secundario en otra vista que tiene desplazamiento. Por lo tanto, anular su método onMeasure 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"); } } }


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


En realidad, el propósito principal de RecyclerView es compensar ListView y ScrollView . En lugar de hacer lo que realmente está haciendo: tener un RecyclerView en un ScrollView , sugeriría tener solo un RecyclerView que pueda manejar muchos tipos de niños.


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

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

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


Esto hace el truco:

recyclerView.setNestedScrollingEnabled(false);


La nueva Biblioteca de soporte de Android 23.2 resuelve ese problema, ahora puede configurar wrap_content como la altura de su RecyclerView y funciona correctamente.

Biblioteca de soporte de Android 23.2


Para ScrollView , puede usar fillViewport=true y hacer layout_height="match_parent" como se muestra a continuación y colocar la vista del reciclador dentro:

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


Parece que NestedScrollView resuelve 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.


Primero debe usar NestedScrollView lugar de ScrollView y colocar RecyclerView dentro de NestedScrollView .

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


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


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


RecyclerViews está bien para poner 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 en 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 deshacerse de RecyclerView en su totalidad e ir con un LinearLayout, inflar subvistas programáticamente y agregarlos al diseño.


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

El problema que obtengo ahora es RecyclerView con layout_height=wrap_content no toma la altura de todos los elementos dentro de ScrollView que solo ocurre en las versiones Marshmallow y Nougat + (API 23, 24, 25).
(ACTUALIZACIÓN: Reemplazar ScrollView con android.support.v4.widget.NestedScrollView funciona en todas las versiones. De alguna manera me perdí la prueba de la solución aceptada . Agregué esto en mi proyecto de github como demo).

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 alternativa es envolver el RecyclerView con 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:


Si coloca RecyclerView dentro de NestedScrollView y habilita recyclerView.setNestedScrollingEnabled(false); , el desplazamiento funcionará bien .
Sin embargo, hay un problema.

RecyclerView no recicla

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

Sin embargo, si en su caso, solo hay unos pocos elementos en RecyclerView y reciclar o no reciclar no afecta mucho el rendimiento, puede usar RecyclerView dentro de ScrollView para simplificar



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