RecyclerView in ScrollView funktioniert nicht

276

Ich versuche, ein Layout zu implementieren, das RecyclerView und ScrollView im selben Layout enthält.

Layoutvorlage:

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

Probleme: Ich kann bis zum letzten Element von scrollen ScrollView

Dinge, die ich versucht habe:

  1. Kartenansicht innerhalb der ScrollView(jetzt ScrollViewenthält RecyclerView) - kann die Karte bis zum sehenRecyclerView
  2. Der erste Gedanke war, dies viewGroupmit zu implementieren , RecyclerViewanstatt ScrollViewwo einer seiner Ansichtstypen der ist, CardViewaber ich habe genau die gleichen Ergebnisse wie mit demScrollView
royB
quelle
15
Eine einfache Lösung in vielen dieser Fälle ist die Verwendung NestedScrollViewstattdessen, da es viele Bildlaufprobleme behandelt
Richard Le Mesurier
Richard gab Ihnen die Antwort im Februar. Verwenden Sie a NestedScrollViewanstelle von a ScrollView. Genau dafür ist es.
Mike M.
2
Ändert nichts für mich.
Luke Allison
2
Wenn bei jemandem ein ähnliches Problem nur bei Marshmallow / Nougat-Geräten (API 23, 24) auftritt
Hossain Khan,

Antworten:

653

verwenden NestedScrollViewstattScrollView

Weitere Informationen finden Sie im NestedScrollView-Referenzdokument .

und recyclerView.setNestedScrollingEnabled(false);zu Ihrem hinzufügenRecyclerView

Yang Peiyong
quelle
24
Funktioniert mit: android.support.v4.widget.NestedScrollView
Cocorico
7
Halten Sie android:layout_height="wrap_content"für das Layout für ViewHolder
Sarath Babu
4
In einem komplexen Layout NestedScrollViewbleibt mir im Gegensatz zum ScrollView. Suche nach einer Lösung ohne VerwendungNestedScrollView
Roman Samoylenko
7
Sie können auch android: nestedScrollingEnabled = "false" zu XML anstelle von recyclerView.setNestedScrollingEnabled (false) hinzufügen;
Kostyabakay
2
Es hat bei mir funktioniert, aber denken Sie daran, dass die Artikel in recyclerView nicht recycelt werden.
Mostafa Arian Nejad
102

Ich weiß, ich bin spät dran, aber das Problem besteht immer noch, auch nachdem Google das Problem behoben hat android.support.v7.widget.RecyclerView

Das Problem , das ich jetzt bekommen , ist RecyclerViewmit layout_height=wrap_contentnicht Höhe aller Elemente Ausgabe nehmen innen , ScrollViewdass nur geschieht auf Eibisch und Nougat + (API 23, 24, 25) Versionen.
(UPDATE: Ersetzen ScrollViewdurch android.support.v4.widget.NestedScrollViewfunktioniert bei allen Versionen. Ich habe es irgendwie verpasst, die akzeptierte Lösung zu testen . Dies wurde in meinem Github-Projekt als Demo hinzugefügt.)

Nachdem ich verschiedene Dinge ausprobiert habe, habe ich eine Problemumgehung gefunden, die dieses Problem behebt.

Hier ist meine Layoutstruktur auf den Punkt gebracht:

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

Die Problemumgehung ist das Umschließen RecyclerViewmit RelativeLayout. Fragen Sie mich nicht, wie ich diese Problemumgehung gefunden habe !!!¯\_(ツ)_/¯

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

Ein vollständiges Beispiel finden Sie im GitHub- Projekt - https://github.com/amardeshbd/android-recycler-view-wrap-content

Hier ist ein Demo-Screencast, der das Update in Aktion zeigt:

Screencast

Hossain Khan
quelle
6
Vielen Dank, Mann. Es hilft sehr. Aber das Scrollen wird nach Ihrer Lösung schwierig, also setze ich recyclerview.setNestedScrollingEnabled (false). und jetzt ist seine Arbeit wie ein Zauber.
Sagar Chavada
4
Recycelt diese Methode Ansichten? wenn wir ungefähr Hunderte von Objekten haben, die recycelt werden müssen. Dies ist keine Lösung.
androidXP
1
Ja, @androidXP ist richtig, dieser Hack ist keine Lösung für eine lange Liste. Mein Anwendungsfall war ein fester Punkt in einer Listenansicht, der kleiner als 10 war. Und wie ich die andere Problemumgehung fand, habe ich zufällige Dinge ausprobiert, dies war eine davon :-)
Hossain Khan
perfect.solved mein Problem in Adnroid Marshmallow und Upper. ;)
Amir Ziarati
2
Wenn ich diese Lösung verwende, wird onBindView für alle Elemente in der Liste aufgerufen, was nicht der Fall von recyclerview ist.
Passagier
54

Obwohl die Empfehlung, dass

Sie sollten niemals eine scrollbare Ansicht in eine andere scrollbare Ansicht einfügen

Ist ein guter Rat, aber wenn Sie eine feste Höhe in der Recycler-Ansicht einstellen, sollte es gut funktionieren.

Wenn Sie die Höhe des Adapterelement-Layouts kennen, können Sie einfach die Höhe des RecyclerView berechnen.

int viewHeight = adapterItemSize * adapterData.size();
recyclerView.getLayoutParams().height = viewHeight;
Joakim Engstrom
quelle
10
Wie bekomme ich adapterItemSize in recyclerview eine Idee?
Krutik
1
Es wirkt wie ein Zauber! Kleine Korrektur sollte es sein: int viewHeight = adapterItemSize * adapterData.size (); recyclerView.getLayoutParams (). height = viewHeight;
Vaibhav Jani
3
Wie finde ich adapterItemSize heraus?
droidster.me
2
@ JoakimEngstrom Was ist die adapterItemSizeVariable?
IgorGanapolsky
1
Vermeiden Sie die Recycler-Ansicht in der Bildlaufansicht, da die Bildlaufansicht dem untergeordneten Element unendlich viel Platz bietet. Dies bewirkt, dass die Recycler-Ansicht mit wrap_content als Höhe unendlich in vertikaler Richtung gemessen wird (bis zum letzten Element der Recycler-Ansicht). Verwenden Sie anstelle der Recycler-Ansicht in der Bildlaufansicht nur die Recycler-Ansicht mit unterschiedlichen Elementtypen. Mit dieser Implementierung würden sich untergeordnete Elemente der Bildlaufansicht als Ansichtstyp verhalten. Behandeln Sie diese Ansichtstypen in der Recycler-Ansicht.
Abhishesh
28

Falls das Festlegen der festen Höhe für RecyclerView für jemanden (wie mich) nicht funktioniert hat, habe ich Folgendes zur Lösung für feste Höhe hinzugefügt:

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

    }
});
Clans
quelle
1
In der Tat hat das bei mir gut funktioniert. Mit dieser Option konnte ich die Bildlaufansicht nach oben und unten verschieben. Wenn ich die Recyclingansicht zum Scrollen auswähle, hat sie Vorrang vor der Bildlaufansicht. Vielen Dank für den Tipp
PGMacDesign
1
Es hat auch bei mir funktioniert. Verwenden Sie getParent nur für ein direktes Kind der Bildlaufansicht
BigBen3216,
Diese Methode funktioniert auch bei Cyanogenmod-Verteilungen. Die Lösung mit fester Höhe für Cyanogenmod funktioniert jedoch nur, wenn die feste Höhe die absolute Höhe aller Elemente in der Liste ist, was den Sinn der Verwendung der Recycling-Ansicht in erster Linie widerspricht. Upvoted.
HappyKatz
Ich brauchte auch recyclerView.setNestedScrollingEnabled (false);
Analizer
15

RecyclerViews können problemlos in ScrollViews eingefügt werden, solange sie nicht selbst scrollen. In diesem Fall ist es sinnvoll, eine feste Höhe festzulegen.

Die richtige Lösung besteht darin, wrap_contentdie RecyclerView-Höhe zu verwenden und dann einen benutzerdefinierten LinearLayoutManager zu implementieren, der die Umhüllung ordnungsgemäß handhaben kann.

Kopieren Sie diesen LinearLayoutManager in Ihr Projekt: https://github.com/serso/android-linear-layout-manager/blob/master/lib/src/main/java/org/solovyev/android/views/llm/LinearLayoutManager.java

Wickeln Sie dann die RecyclerView ein:

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

Und richten Sie es so ein:

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

Bearbeiten: Dies kann zu Komplikationen beim Scrollen führen, da RecyclerView die Berührungsereignisse von ScrollView stehlen kann. Meine Lösung bestand darin, die RecyclerView in allen Bereichen zu löschen und ein LinearLayout zu verwenden, Unteransichten programmgesteuert aufzublasen und sie dem Layout hinzuzufügen.

jcady
quelle
4
Könnten Sie setNestedScrollingEnabled (false) in der Recyclingansicht nicht aufrufen?
Eshaan
14

Für ScrollView, Sie könnten wie unten verwenden fillViewport=trueund machen layout_height="match_parent"und Recycler-Ansicht nach innen setzen:

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

Keine weitere Höheneinstellung durch Code erforderlich.

mustaq
quelle
Getestet funktioniert gut mit v23.2.1. Verwendete es zum Hinzufügen eines Layouts über der Recyclingansicht.
Winsontan520
nur das RecyclerView Scrollen und das ScrollView nicht scrollen
Samad
13

RecyclerViewDie manuelle Berechnung der Höhe ist nicht gut, es ist besser, eine benutzerdefinierte zu verwenden LayoutManager.

Der Grund für die oben genannte Problem ist jede Ansicht , die es des Scroll hat ( ListView, GridView, RecyclerView) konnte es in der Höhe berechnen , wenn Add als Kind in einer anderen Ansicht blättern hat. Das Überschreiben der onMeasureMethode löst das Problem.

Bitte ersetzen Sie den Standard-Layout-Manager durch den folgenden:

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");
    }
}
}
Arun Antoney
quelle
Wie kann ich diese Klasse aufrufen, wenn wir die Daten auf Adapter setzen?
Milan Gajera
11

Versuche dies. Sehr späte Antwort. Aber sicherlich in Zukunft jedem helfen.

Stellen Sie Ihre Bildlaufansicht auf NestedScrollView ein

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

In Ihrem Recyclerview

recyclerView.setNestedScrollingEnabled(false); 
recyclerView.setHasFixedSize(false);
Ranjith Kumar
quelle
8
Wenn Sie RecyclerView in NestedScrollView verwenden, wird onBindView für jedes Element in der Liste aufgerufen, auch wenn das Element nicht sichtbar ist. Irgendeine Lösung für dieses Problem?
Passagier
8

UPDATE: Diese Antwort ist veraltet, da es Widgets wie NestedScrollView und RecyclerView gibt, die das verschachtelte Scrollen unterstützen.

Sie sollten niemals eine scrollbare Ansicht in eine andere scrollbare Ansicht einfügen!

Ich schlage vor, Sie erstellen Ihre Hauptlayout-Recycler-Ansicht und setzen Ihre Ansichten als Elemente der Recycler-Ansicht ein.

Schauen Sie sich dieses Beispiel an, in dem gezeigt wird, wie mehrere Ansichten im Recycler-Ansichtsadapter verwendet werden. Link zum Beispiel

EC84B4
quelle
Ich habe eine Seite mit mehr als einem Recycler. Gibt es eine andere Möglichkeit, dies zu überzeugen? etwas wie Instagram oder Google Play Part Kommentar, die mehr Datensatz laden, wenn Sie auf mehr Kommentar
Ashkan
Machen Sie es zu einem einzigen RecyclerView und setzen Sie Ihre Ansichten als Elemente für diesen Recycler
EC84B4
6
Das ist Unsinn. Sie können RecyclerViews problemlos verschachteln.
Igor Ganapolsky
Diese Antwort ist jetzt veraltet. Wir haben Dinge wie NestedScrollView, die verschachtelte scrollbare Ansichten ermöglichen. Verschachtelte RecyclerViews funktionieren jetzt auch.
Corey Horn
7

Wenn Sie RecyclerViewhineinstecken NestedScrollViewund aktivieren recyclerView.setNestedScrollingEnabled(false);, funktioniert das Scrollen gut .
Es gibt jedoch ein Problem

RecyclerView nicht recyceln

Zum Beispiel haben Ihre RecyclerView(innerhalb NestedScrollViewoder ScrollView) 100 Artikel.
Beim ActivityStart werden 100 Elemente erstellt ( onCreateViewHolderund onBindViewHoldervon 100 Elementen werden gleichzeitig aufgerufen).
Beispiel: Für jedes Element laden Sie ein großes Bild aus der API => Aktivität erstellt -> 100 Bilder werden geladen.
Es macht die Startaktivität langsamer und verzögert.
Mögliche Lösung :
- Denken Sie über die Verwendung RecyclerViewmit mehreren Typen nach.

Wenn jedoch in Ihrem Fall, es gibt nur ein paar Artikel in RecyclerViewund recyceln oder entsorge nicht nicht viel Einfluss auf die Leistung, können Sie RecyclerViewinnen ScrollViewfür einfache

Phan Van Linh
quelle
Zeigen Sie, wie ich RecyclerView auch in ScollView recyceln kann. Vielen Dank!
Lügner
2
@Liar, derzeit gibt es keine Möglichkeit, das RecyclerViewRecycling nach dem Ablegen durchzuführen ScrollView. Wenn Sie recyceln möchten, denken Sie über einen anderen Ansatz nach (wie die Verwendung von RecyclerView mit mehreren Typen)
Phan Van Linh
4

Sie können diesen Weg entweder verwenden:

Fügen Sie diese Zeile Ihrer recycelView-XML-Ansicht hinzu:

        android:nestedScrollingEnabled="false"

Probieren Sie es aus, recyclerview wird reibungslos mit flexibler Höhe gescrollt

hoffe das hat geholfen.

iDeveloper
quelle
2

Es scheint, dass NestedScrollViewdas Problem dadurch gelöst wird. Ich habe mit diesem Layout getestet:

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

Und es funktioniert ohne Probleme

royB
quelle
Bro, habe immer noch das gleiche Problem, nachdem ich aus der Scrollview in NestedScrollview gewechselt habe.
MohanRaj S
mmm ... können Sie etwas Code teilen ... Ich habe keine Probleme, aber Sie können nie mit dieser Art von Problemen wissen
RoyB
Wie kann ich meinen Code per E-Mail oder per Stapelüberlauf teilen
MohanRaj S
2
Wenn Sie diesen Code verwenden, wird onBindView für alle Elemente in der Liste aufgerufen, auch wenn diese Elemente in der Liste nicht sichtbar sind. Dies macht den Zweck der Recyclingübersicht zunichte.
Passagier
2

Ich habe CustomLayoutManager verwendet, um RecyclerView Scrolling zu deaktivieren. Verwenden Sie die Recycler-Ansicht auch nicht als WrapContent, sondern als 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();
    }
}

Verwenden Sie 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>
Hamza Khan
quelle
2

Ich hatte das gleiche Problem. Das habe ich versucht und es funktioniert. Ich teile meinen XML- und Java-Code. Hoffe das wird jemandem helfen.

Hier ist die 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 >

Hier ist der zugehörige Java-Code. Es wirkt wie ein Zauber.

LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setNestedScrollingEnabled(false);
Zeeshan Shabbir
quelle
Was ist mit Layouts unter RecyclerView?
Tapa Save
das funktioniert auch. Sie müssen nur verwenden. NestedScrollViewanstelle der Bildlaufansicht
Zeeshan Shabbir
Ja, nur NestedScrollView. Aber Lösung mit NestedScrollView + RecycleView ruft sehr langsame Populationen von RecyclerView auf (ich denke für die Berechnung der Y-Position der ersten Ansicht nach RecyclerView)
Tapa Save
1

Das macht den Trick:

recyclerView.setNestedScrollingEnabled(false);
Shylendra Madda
quelle
0

Eigentlich ist der Hauptzweck der RecyclerViewist zu kompensieren ListViewund ScrollView. Anstatt das zu tun, was Sie tatsächlich tun: Ein RecyclerViewin einem zu haben ScrollView, würde ich vorschlagen, nur ein zu haben RecyclerView, das mit vielen Arten von Kindern umgehen kann.

Samer
quelle
1
Dies würde nur funktionieren, vorausgesetzt, Ihre Kinder können gesammelt werden, sobald Sie sie außer Sichtweite scrollen. Wenn Sie Kinder haben, bei denen es sich um mapFragments oder Streetviews handelt, ist dies nicht sinnvoll, da sie jedes Mal neu geladen werden müssen, wenn sie aus der Recycling-Ansicht scrollen. Es ist dann sinnvoller, sie in eine Bildlaufansicht einzubetten und dann unten eine Recyclingansicht zu erstellen.
Simon
1
@ Simon gibt es handliche setIsRecyclable () in ViewHolder
ernazm
RecyclerView ersetzt ListView und soll ScrollView nicht ersetzen.
Cyroxis
Dieser Kommentar verdient es besser. Es ist eine Lösung und sogar besser als die akzeptierte.
Kai Wang
0

Zuerst sollten Sie NestedScrollViewstatt verwenden ScrollViewund das RecyclerViewInnere setzen NestedScrollView.

Verwenden Sie die benutzerdefinierte Layoutklasse, um die Höhe und Breite des Bildschirms zu messen:

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

Und implementieren Sie den folgenden Code in die Aktivität / das Fragment von 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!");
            }
        }
    });
Soni Kumar
quelle
0

Wenn RecyclerView nur eine Zeile in ScrollView anzeigt. Sie müssen nur die Höhe Ihrer Zeile auf einstellen android:layout_height="wrap_content".

SANAT
quelle
0

Sie können LinearLayoutManager auch überschreiben, damit die Recycling-Ansicht reibungslos funktioniert

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

Es tut mir leid, dass ich zu spät zur Party komme, aber es scheint, als gäbe es eine andere Lösung, die für den von Ihnen erwähnten Fall perfekt funktioniert.

Wenn Sie eine Recycler-Ansicht in einer Recycler-Ansicht verwenden, scheint dies einwandfrei zu funktionieren. Ich habe es persönlich versucht und benutzt, und es scheint überhaupt keine Langsamkeit und kein Ruckeln zu geben. Jetzt bin ich mir nicht sicher, ob dies eine gute Vorgehensweise ist oder nicht, aber das Verschachteln mehrerer Recycler-Ansichten, selbst verschachtelte Bildlaufansichten, verlangsamt sich. Aber das scheint gut zu funktionieren. Bitte probieren Sie es aus. Ich bin mir sicher, dass das Verschachteln damit vollkommen in Ordnung sein wird.

Shailendra Pundhir
quelle
0

Ein anderer Ansatz zur Behebung des Problems besteht darin, ConstraintLayoutFolgendes zu verwenden ScrollView:

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

Aber ich würde mich immer noch an den androidx.core.widget.NestedScrollView von Yang Peiyong vorgeschlagenen Ansatz halten .

Soshial
quelle
-2

** Lösung, die bei mir funktioniert hat
Verwenden Sie NestedScrollView mit der Höhe als 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"

// Dein Zeileninhalt geht hier

maruti060385
quelle
Einige Kommentare zu einer ähnlichen Antwort besagen, dass das Deaktivieren des verschachtelten Bildlaufs den Zweck der Verwendung einer RecyclerView zunichte macht, dh, dass Ansichten nicht recycelt werden. Ich bin mir nicht sicher, wie ich das bestätigen soll.
jk7
1
Das Setzen von nestedScrollingEnabled = "false" bewirkt, dass RecyclerView seine Ansichten zumindest in meinem Setup (eine RecyclerView in einer NestedScrollView) NICHT wiederverwendet. Bestätigt durch Hinzufügen eines RecyclerListener zur RecyclerView und Festlegen eines Haltepunkts in der onViewRecycled () -Methode.
jk7