RecyclerView all'interno di ScrollView non funziona


278

Sto cercando di implementare un layout che contiene RecyclerView e ScrollView nello stesso layout.

Modello di layout:

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

Problemi: posso scorrere fino all'ultimo elemento di ScrollView

Cose che ho provato:

  1. la scheda all'interno del ScrollView(ora ScrollViewcontiene RecyclerView) - può vedere la carta fino alRecyclerView
  2. il pensiero iniziale era di implementarlo viewGroupusando RecyclerViewinvece di ScrollViewdove uno dei suoi punti di vista è il tipo CardViewma ho ottenuto gli stessi esatti risultati con ilScrollView


15
una soluzione semplice in molti di questi casi è NestedScrollViewinvece quella di utilizzare , poiché gestisce molti problemi di scorrimento
Richard Le Mesurier,

Richard ti ha dato la risposta a febbraio. Usa a NestedScrollViewinvece di a ScrollView. Questo è esattamente ciò che serve.
Mike M.,

2
Non cambia nulla per me.
Luke Allison,

2
Per riferimento futuro, se qualcuno riscontra un problema simile solo su dispositivi marshmallow / nougat (API 23, 24), controlla la mia soluzione alternativa su stackoverflow.com/a/38995399/132121
Hossain Khan,

Risposte:


654

utilizzare NestedScrollViewinvece diScrollView

Per ulteriori informazioni, consultare il documento di riferimento di NestedScrollView .

e aggiungi recyclerView.setNestedScrollingEnabled(false);al tuoRecyclerView


24
Funziona con: android.support.v4.widget.NestedScrollView
Cocorico,

7
mantenere android:layout_height="wrap_content"per il layout gonfiato per ViewHolder
Sarath Babu

4
In un layout complesso NestedScrollViewè in ritardo per me, a differenza del ScrollView. Alla ricerca di una soluzione senza usareNestedScrollView
Roman Samoylenko il

7
Inoltre puoi aggiungere android: nestedScrollingEnabled = "false" in XML invece di recyclerView.setNestedScrollingEnabled (false);
kostyabakay,

2
Ha funzionato per me, ma tieni presente che gli articoli all'interno di recyclerView non vengono riciclati.
Mostafa Arian Nejad,

102

So di essere in ritardo nel gioco, ma il problema persiste anche dopo che Google ha risolto il problema android.support.v7.widget.RecyclerView

Il problema ottengo ora è RecyclerViewdi layout_height=wrap_contentnon prendere altezza di tutto il problema voci all'interno ScrollViewche solo accade nelle versioni Marshmallow e Torrone + (API 23, 24, 25).
(AGGIORNAMENTO: Sostituzione ScrollViewcon android.support.v4.widget.NestedScrollViewopere su tutte le versioni. In qualche modo ho perso la prova accettata soluzione . Aggiunto questo nel mio progetto github come demo.)

Dopo aver provato diverse cose, ho trovato una soluzione alternativa che risolve questo problema.

Ecco la mia struttura di layout in breve:

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

La soluzione alternativa è l'avvolgere il RecyclerViewcon RelativeLayout. Non chiedermi come ho trovato questa soluzione alternativa !!!¯\_(ツ)_/¯

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

L'esempio completo è disponibile sul progetto GitHub - https://github.com/amardeshbd/android-recycler-view-wrap-content

Ecco uno screencast demo che mostra la correzione in azione:

screencast


6
Grazie amico .. è di grande aiuto .. ma lo scorrimento diventa difficile dopo la tua soluzione, quindi ho impostato recyclerview.setNestedScrollingEnabled (false); e ora funziona come un fascino.
Sagar Chavada,

4
Questo metodo ricicla le viste? se abbiamo circa centinaia di oggetti da riciclare. Questa non è una soluzione hack.
androidXP,

1
Sì, @androidXP ha ragione, questo trucco non è una soluzione per un lungo elenco. Il mio caso d'uso era un elemento fisso in una vista elenco che era inferiore a 10. E per quanto riguarda il modo in cui ho trovato l'altra soluzione alternativa, stavo provando cose a caso, questo era uno di loro :-)
Hossain Khan

perfect.solved my problem in adnroid marshmallow and upper. ;)
Amir Ziarati,

2
se uso questa soluzione, allora onBindView viene chiamato per tutti gli elementi dell'elenco, che non è il caso d'uso di recyclerview.
thedarkpassenger,

54

Anche se la raccomandazione che

non dovresti mai mettere una vista scorrevole all'interno di un'altra vista scorrevole

È un buon consiglio, tuttavia se si imposta un'altezza fissa sulla vista del riciclo, dovrebbe funzionare bene.

Se conosci l'altezza del layout dell'articolo dell'adattatore, puoi semplicemente calcolare l'altezza di RecyclerView.

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

10
Come ottenere adapterItemSize in Recyclerview qualche idea?
Krutik,

1
Esso funziona magicamente! Piccola correzione dovrebbe essere: int viewHeight = adapterItemSize * adapterData.size (); recyclerView.getLayoutParams (). height = viewHeight;
Vaibhav Jani,

3
Come scoprire adapterItemSize?
droidster.me

2
@JoakimEngstrom Qual è la adapterItemSizevariabile?
IgorGanapolsky,

1
Evita la vista riciclatore all'interno della vista Scorrimento, perché la vista di scorrimento offre al suo bambino uno spazio infinito. Questo fa sì che la vista del riciclatore abbia wrap_content come altezza per misurare all'infinito in direzione verticale (fino all'ultimo elemento della vista del riciclatore). Invece di utilizzare la vista Riciclatore all'interno della vista a scorrimento, utilizzare solo la vista Riciclatore con diversi tipi di elementi. Con questa implementazione, i figli della vista di scorrimento si comporterebbero come tipo di vista. Gestisci quei tipi di vista all'interno della vista del riciclatore.
abhishesh,

28

Nel caso in cui l'impostazione dell'altezza fissa per RecyclerView non funzionasse per qualcuno (come me), ecco cosa ho aggiunto alla soluzione ad altezza fissa:

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
In effetti, questo ha funzionato bene per me. Con questo in atto, sono stato in grado di spostare la vista di scorrimento su e giù, e quando seleziono la vista riciclo per lo scorrimento, ha la priorità sulla vista di scorrimento. Grazie per la punta
PGMacDesign il

1
ha funzionato anche per me, assicurati di usare getParent su un figlio diretto della vista di scorrimento
BigBen3216

Questo metodo funziona anche sulle distribuzioni di cianogenmod. La soluzione ad altezza fissa su cyanogenmod funziona, ma solo se l'altezza fissa è l'altezza assoluta di tutti gli elementi dell'elenco, il che in primo luogo sfida il punto di usare il recyclerview. Upvoted.
HappyKatz,

Avevo anche bisogno di recyclerView.setNestedScrollingEnabled (false);
Analizzatore


15

RecyclerView consente di inserire le ScrollView purché non si stiano scorrendo da sole. In questo caso, ha senso renderlo ad altezza fissa.

La soluzione corretta è utilizzare wrap_contentl'altezza di RecyclerView e quindi implementare un LinearLayoutManager personalizzato in grado di gestire correttamente l'avvolgimento.

Copia questo LinearLayoutManager nel tuo progetto: https://github.com/serso/android-linear-layout-manager/blob/master/lib/src/main/java/org/solovyev/android/views/llm/LinearLayoutManager.java

Quindi avvolgere RecyclerView:

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

E impostalo in questo modo:

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

Modifica: questo può causare complicazioni con lo scorrimento perché RecyclerView può rubare gli eventi tattili di ScrollView. La mia soluzione era solo di abbandonare il RecyclerView in tutto e andare con un LinearLayout, gonfiare a livello di codice le visualizzazioni secondarie e aggiungerle al layout.


4
Non è possibile chiamare setNestedScrollingEnabled (false) sul recyclerview?
Eshaan,

14

Per ScrollView, puoi usare fillViewport=truee creare layout_height="match_parent"come di seguito e inserire la vista del riciclatore all'interno:

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

Non sono necessarie ulteriori regolazioni dell'altezza tramite il codice.


Testato perfettamente funzionante con v23.2.1. Lo stava usando per aggiungere layout sopra recyclerview.
winsontan520

solo lo scorrimento di RecyclerView e lo scorrimento di ScrollView non scorre
Samad,

13

Calcolare RecyclerViewl'altezza manualmente non è buono, è meglio usare un'abitudine LayoutManager.

La ragione per la questione di cui sopra è un qualsiasi punto di vista che ha il rotolo ( ListView, GridView, RecyclerView) non è riuscito a calcolarlo di altezza quando aggiuntivo come un bambino in un altro punto di vista ha di scorrimento. Quindi ignorare il suo onMeasuremetodo risolverà il problema.

Sostituisci il layout manager predefinito con il seguente:

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

Come posso chiamare questa classe quando impostiamo i dati su adattatore?
Milan Gajera,

11

Prova questo. Risposta molto tardi. Ma sicuramente aiuteremo chiunque in futuro.

Imposta Scrollview su NestedScrollView

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

Nel tuo Recyclerview

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

8
l'utilizzo di RecyclerView in NestedScrollView chiama onBindView per ogni elemento nell'elenco anche se l'elemento non è visibile. Qualche soluzione per quel problema?
thedarkpassenger,

8

AGGIORNAMENTO: questa risposta non è più aggiornata in quanto vi sono widget come NestedScrollView e RecyclerView che supportano lo scorrimento nidificato.

non dovresti mai mettere una vista scorrevole all'interno di un'altra vista scorrevole!

ti suggerisco di creare la vista del riciclatore del layout principale e di inserire le viste come elementi della vista del riciclatore.

Dai un'occhiata a questo esempio che mostra come utilizzare più viste all'interno dell'adattatore della vista del riciclatore. collegamento all'esempio


ho una pagina con più di un riciclatore , c'è qualche altro modo per convincerlo? qualcosa come instagram o google gioca un commento parte che carica più record quando fai clic su più commenti
Ashkan,


6
Questa è una sciocchezza. Puoi avere nidificato RecyclerView correttamente.
IgorGanapolsky,

Questa risposta è ora obsoleta. Abbiamo cose come NestedScrollView che consentono visualizzazioni scorrevoli nidificate. Anche le viste Recycler nidificate ora funzionano.
Corey Horn,

7

Se lo metti RecyclerViewdentro NestedScrollViewe lo abiliti recyclerView.setNestedScrollingEnabled(false);, lo scorrimento funzionerà bene .
Tuttavia, c'è un problema

RecyclerView non riciclare

Ad esempio, il tuo RecyclerView(interno NestedScrollViewo ScrollView) ha 100 oggetti.
All'avvio Activity, verranno creati 100 oggetti ( onCreateViewHoldere onBindViewHolderdi 100 oggetti richiamati contemporaneamente).
Ad esempio, per ciascun elemento, verrà caricata un'immagine grande dall'API => attività creata -> verrà caricata l'immagine 100.
Rende lenta e ritardata l'attività iniziale.
Possibile soluzione :
- Pensando all'utilizzo RecyclerViewcon più tipi.

Tuttavia, se nel tuo caso, ci sono solo pochi elementi dentro RecyclerViewe riciclare o non riciclare non influisce molto sulle prestazioni, puoi usare RecyclerViewdentro ScrollViewper semplice


Mostra come posso riciclare RecyclerView anche all'interno di ScollView? Grazie!
Bugiardo

2
@Liar, al momento non c'è modo di RecyclerViewriciclare dopo averlo inserito ScrollView. Se vuoi riciclare, pensando a un altro approccio (come usare RecyclerView con più tipi)
Phan Van Linh,

4

Puoi usare in questo modo:

Aggiungi questa linea alla vista xml di recyclerView:

        android:nestedScrollingEnabled="false"

provalo, il recyclerview verrà fatto scorrere agevolmente con un'altezza flessibile

spero che questo abbia aiutato.


2

Sembra che NestedScrollViewrisolva il problema. Ho provato usando questo layout:

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

E funziona senza problemi


Fratello, ho ancora lo stesso problema dopo aver cambiato in NestedScrollview da Scrollview.
MohanRaj S

mmm ... puoi condividere un po 'di codice ... Sto avendo zero problemi ma non puoi mai sapere con questo tipo di problemi
royB

come posso condividere il mio codice via e-mail o tramite overflow dello stack
MohanRaj S

2
l'utilizzo di questo codice chiama onBindView per tutti gli elementi nell'elenco anche se tali elementi non sono visibili nell'elenco. Ciò vanifica lo scopo di recyclerview.
thedarkpassenger,

2

Ho usato CustomLayoutManager per disabilitare lo scorrimento di RecyclerView. Inoltre, non utilizzare Recycler View come WrapContent, usalo come 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();
    }
}

Usa CustomLayoutManager in 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

Avevo lo stesso problema. Questo è quello che ho provato e funziona. Sto condividendo il mio codice XML e Java. Spero che questo possa aiutare qualcuno.

Ecco l'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 >

Ecco il codice Java correlato. Esso funziona magicamente.

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

Che dire dei layout sotto RecyclerView?
Tapa Salva il

funziona pure. Devi solo usare. NestedScrollViewinvece della vista di scorrimento
Zeeshan Shabbir,

Sì, solo NestedScrollView. Ma la soluzione con NestedScrollView + RecycleView chiama popolazioni molto lente di RecyclerView (penso per il calcolo della posizione Y della prima vista dopo RecyclerView)
Tapa Salva il


0

In realtà lo scopo principale del RecyclerViewè quello di compensare ListViewe ScrollView. Invece di fare quello che stai effettivamente facendo: Avere un RecyclerViewin a ScrollView, suggerirei di avere solo un RecyclerViewche può gestire molti tipi di bambini.


1
Questo funzionerebbe solo a condizione che i tuoi figli possano essere raccolti in modo confuso non appena li fai scorrere fuori dalla vista. Se hai figli che sono mapFragments o streetview, non ha senso in quanto sono costretti a ricaricarsi ogni volta che scorrono dal recyclerview. Incorporarli in una vista di scorrimento e quindi generare una vista di riciclo in fondo ha più senso allora.
Simon,

1
@Simon c'è utile setIsRecyclable () in ViewHolder
ernazm

RecyclerView sostituisce ListView non intende sostituire ScrollView.
Ciroxis,

Questo commento merita di meglio. È una soluzione e persino migliore di quella accettata.
Kai Wang,

0

Per prima cosa dovresti usare NestedScrollViewinvece di ScrollViewe mettere l' RecyclerViewinterno NestedScrollView.

Utilizzare la classe di layout personalizzata per misurare l'altezza e la larghezza dello schermo:

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 implementare sotto il codice nell'attività / frammento di 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

Se RecyclerView mostra solo una riga all'interno di ScrollView. Devi solo impostare l'altezza della riga su android:layout_height="wrap_content".


0

è inoltre possibile ignorare LinearLayoutManager per far scorrere agevolmente la vista riciclo

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

0

Mi dispiace essere in ritardo alla festa, ma sembra che ci sia un'altra soluzione che funziona perfettamente per il caso che hai citato.

Se si utilizza una vista di riciclaggio all'interno di una vista di riciclaggio, sembra funzionare perfettamente. L'ho provato e usato personalmente, e sembra non dare alcuna lentezza o scatti. Ora non sono sicuro che si tratti di una buona pratica o meno, ma la nidificazione di più visualizzazioni di riciclaggio, anche la visualizzazione di scorrimento nidificata rallenta. Ma questo sembra funzionare bene. Per favore, provalo. Sono sicuro che la nidificazione andrà benissimo con questo.


0

Un altro approccio per affrontare il problema è utilizzare ConstraintLayoutall'interno ScrollView:

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

Ma mi androidx.core.widget.NestedScrollView atterrei ancora all'approccio, proposto da Yang Peiyong .


-2

** Soluzione che ha funzionato per me
Usa NestedScrollView con altezza come 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"

// Il contenuto della tua riga va qui


Alcuni commenti su una risposta simile affermano che disabilitare lo scorrimento nidificato vanifica lo scopo di utilizzare RecyclerView, ovvero che non riciclerà le viste. Non sono sicuro di come confermarlo.
jk7

1
L'impostazione di nestedScrollingEnabled = "false" fa sì che RecyclerView NON ricicli le sue viste, almeno nella mia configurazione (un RecyclerView all'interno di un NestedScrollView). Confermato aggiungendo un RecyclerListener a RecyclerView e impostando un punto di interruzione all'interno del suo metodo onViewRecycled ().
jk7,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.