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é:
-
vista de tarjeta dentro de
ScrollView
(ahoraScrollView
contieneRecyclerView
): puede ver la tarjeta hacia arriba hastaRecyclerView
-
La idea inicial era implementar este
viewGroup
usandoRecyclerView
lugar deScrollView
donde una de sus vistas esCardView
pero obtuve exactamente los mismos resultados que conScrollView
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.
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
use
NestedScrollView
lugar de
ScrollView
Consulte el documento de referencia NestedScrollView para obtener más información.
y agregue
recyclerView.setNestedScrollingEnabled(false);
a su
RecyclerView
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