RecyclerView di dalam ScrollView tidak berfungsi

278

Saya mencoba menerapkan tata letak yang berisi RecyclerView dan ScrollView pada tata letak yang sama.

Template tata letak:

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

Masalah: saya dapat menggulir hingga elemen terakhir dari ScrollView

Hal yang saya coba:

  1. tampilan kartu di dalam ScrollView(sekarang ScrollViewberisi RecyclerView) - dapat melihat kartu hinggaRecyclerView
  2. pemikiran awal adalah untuk mengimplementasikan ini viewGroupmenggunakan RecyclerViewbukan di ScrollViewmana salah satu jenis pandangan itu adalah CardViewtetapi saya mendapat hasil yang sama persis denganScrollView
royB
sumber
lihat pendekatan ini: stackoverflow.com/a/21878703/684582
Alécio Carvalho
15
solusi sederhana dalam banyak kasus ini adalah dengan menggunakan NestedScrollView, karena menangani banyak masalah gulir
Richard Le Mesurier
Richard memberi Anda jawabannya pada bulan Februari. Gunakan a NestedScrollViewbukan a ScrollView. Itulah tepatnya untuk apa.
Mike M.
2
Tidak mengubah apa pun untuk saya.
Luke Allison
2
Untuk referensi di masa mendatang, jika ada orang yang mengalami masalah yang serupa hanya perangkat marshmallow / nougat (API 23, 24), periksa solusi saya di stackoverflow.com/a/38995399/132121
Hossain Khan

Jawaban:

654

gunakan NestedScrollViewsebagai gantiScrollView

Silakan buka dokumen referensi NestedScrollView untuk informasi lebih lanjut.

dan tambahkan recyclerView.setNestedScrollingEnabled(false);keRecyclerView

Yang Peiyong
sumber
24
Bekerja dengan: android.support.v4.widget.NestedScrollView
Cocorico
7
terus android:layout_height="wrap_content"untuk tata letak yang meningkat untuk ViewHolder
Sarath Babu
4
Dalam tata letak yang rumit NestedScrollViewtertinggal bagi saya, tidak seperti ScrollView. Mencari solusi tanpa menggunakanNestedScrollView
Roman Samoylenko
7
Anda juga dapat menambahkan android: nestedScrollingEnabled = "false" ke XML alih-alih recyclerView.setNestedScrollingEnabled (false);
kostyabakay
2
Itu bekerja untuk saya tetapi perlu diingat bahwa item di dalam recyclerView tidak didaur ulang.
Mostafa Arian Nejad
102

Saya tahu saya terlambat bermain, tetapi masalah masih ada bahkan setelah google telah memperbaiki android.support.v7.widget.RecyclerView

Masalah yang saya dapatkan sekarang adalah RecyclerViewdengan layout_height=wrap_contenttidak mempertimbangkan semua masalah item di dalam ScrollViewyang hanya terjadi pada versi Marshmallow dan Nougat + (API 23, 24, 25).
(UPDATE: Mengganti ScrollViewdengan android.support.v4.widget.NestedScrollViewkarya pada semua versi. Saya entah bagaimana melewatkan pengujian solusi yang diterima . Menambahkan ini dalam proyek github saya sebagai demo.)

Setelah mencoba berbagai hal, saya telah menemukan solusi yang memperbaiki masalah ini.

Berikut ini adalah singkatnya struktur tata letak saya:

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

Solusinya adalah bungkus RecyclerViewdengan RelativeLayout. Jangan tanya saya bagaimana saya menemukan solusi ini !!!¯\_(ツ)_/¯

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

Contoh lengkap tersedia di proyek GitHub - https://github.com/amardeshbd/android-recycler-view-wrap-content

Berikut adalah demo layar menunjukkan perbaikan dalam tindakan:

Screencast

Hossain Khan
sumber
6
Terima kasih sobat .. ini sangat membantu .. tetapi menggulung menjadi sulit setelah solusi Anda jadi saya mengatur recyclerview.setNestedScrollingEnabled (false); dan sekarang ini berfungsi seperti pesona.
Sagar Chavada
4
Apakah metode ini mendaur ulang tampilan? jika kita memiliki sekitar ratusan objek untuk didaur ulang. Ini bukan solusi hack.
androidXP
1
Ya, @androidXP benar, peretasan ini bukan solusi untuk daftar panjang. Kasus penggunaan saya adalah item tetap dalam tampilan daftar yang kurang dari 10. Dan tentang bagaimana saya menemukan solusi lain, saya mencoba hal-hal acak, ini adalah salah satunya :-)
Hossain Khan
perfect.solved masalah saya di adnroid marshmallow dan atas. ;)
Amir Ziarati
2
jika saya menggunakan solusi ini, maka onBindView dipanggil untuk semua item dalam daftar, yang bukan merupakan usecase dari recyclerview.
penumpang utama
54

Meskipun rekomendasi itu

Anda tidak boleh meletakkan tampilan yang dapat digulir di dalam tampilan yang dapat digulir

Merupakan saran yang bagus, namun jika Anda menetapkan ketinggian tetap pada tampilan pendaur ulang, itu akan berfungsi dengan baik.

Jika Anda tahu ketinggian tata letak item adaptor, Anda bisa menghitung ketinggian RecyclerView.

int viewHeight = adapterItemSize * adapterData.size();
recyclerView.getLayoutParams().height = viewHeight;
Joakim Engstrom
sumber
10
Bagaimana cara mendapatkan adapterItemSize di recyclerview ide?
Krutik
1
Itu bekerja seperti pesona! Koreksi kecil seharusnya: int viewHeight = adapterItemSize * adapterData.size (); recyclerView.getLayoutParams (). height = viewHeight;
Vaibhav Jani
3
Bagaimana cara mengetahui adapterItemSize?
droidster.me
2
@ JoakimEngstrom Apa adapterItemSizevariabelnya?
IgorGanapolsky
1
Hindari tampilan pendaur ulang di dalam tampilan Gulir, karena tampilan gulir memberi ruang tak terbatas kepada anaknya. Ini menyebabkan tampilan pendaur ulang memiliki wrap_content sebagai ketinggian untuk mengukur secara tak terbatas dalam arah vertikal (sampai item terakhir dari tampilan pendaur ulang). Alih-alih menggunakan tampilan pendaur ulang di dalam tampilan gulir, gunakan hanya tampilan pendaur ulang dengan jenis item yang berbeda. Dengan implementasi ini, anak-anak dari tampilan gulir akan berperilaku sebagai tipe tampilan. Tangani viewtypes tersebut di dalam view recycler.
abhishesh
28

Dalam hal mengatur ketinggian tetap untuk RecyclerView tidak berfungsi untuk seseorang (seperti saya), berikut adalah apa yang saya tambahkan ke solusi ketinggian tetap:

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

    }
});
Klan
sumber
1
Memang, ini bekerja dengan baik untuk saya. Dengan ini, saya dapat memindahkan tampilan gulir ke atas dan ke bawah, dan ketika saya memilih recyclerview untuk menggulir, dibutuhkan prioritas daripada tampilan gulir. Terima kasih atas tipnya
PGMacDesign
1
itu bekerja untuk saya juga, pastikan untuk menggunakan getParent pada anak langsung dari scrollview
BigBen3216
Metode ini juga berfungsi pada distribusi cyanogenmod. Solusi ketinggian tetap pada cyanogenmod berfungsi, tetapi hanya jika ketinggian tetap adalah ketinggian absolut dari semua item dalam daftar, yang menentang titik penggunaan recyclerview di tempat pertama. Terpilih.
HappyKatz
Saya juga membutuhkan recyclerView.setNestedScrollingEnabled (false);
Analizer
15

RecyclerViews boleh dimasukkan ke ScrollViews selama mereka tidak menggulir diri. Dalam hal ini, masuk akal untuk membuatnya tinggi tetap.

Solusi yang tepat adalah menggunakan wrap_contentpada ketinggian RecyclerView dan kemudian mengimplementasikan LinearLayoutManager kustom yang dapat menangani pembungkus dengan benar.

Salin LinearLayoutManager ini ke proyek Anda: https://github.com/serso/android-linear-layout-manager/blob/master/lib/src/main/java/org/solovyev/android/views/llm/LinearLayoutManager.java

Kemudian bungkus RecyclerView:

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

Dan atur seperti ini:

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

Sunting: Ini dapat menyebabkan komplikasi dengan menggulir karena RecyclerView dapat mencuri acara sentuh ScrollView. Solusi saya adalah hanya membuang RecyclerView di semua dan pergi dengan LinearLayout, secara terprogram mengembang subviews, dan menambahkannya ke tata letak.

jcady
sumber
4
Tidak bisakah Anda memanggil setNestedScrollingEnabled (false) pada recyclerview?
Eshaan
14

Sebab ScrollView, Anda bisa menggunakan fillViewport=truedan membuat layout_height="match_parent"seperti di bawah ini dan meletakkan tampilan pendaur ulang di dalamnya:

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

Tidak diperlukan penyesuaian ketinggian lebih lanjut melalui kode.

mustaq
sumber
Diuji bekerja dengan baik menggunakan v23.2.1. Sedang menggunakannya untuk menambahkan tata letak di atas recyclerview.
winsontan520
hanya bergulir RecyclerView dan ScrollView tidak menggulir
Samad
13

Menghitung RecyclerViewtinggi secara manual tidak baik, lebih baik menggunakan kebiasaan LayoutManager.

Alasan untuk masalah di atas adalah pandangan yang memiliki gulir itu ( ListView, GridView, RecyclerView) gagal untuk menghitung tinggi itu ketika add sebagai anak dalam pandangan lain memiliki gulir. Jadi mengganti onMeasuremetodenya akan menyelesaikan masalah.

Silakan ganti manajer tata letak default dengan yang di bawah ini:

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
sumber
Bagaimana saya bisa memanggil kelas ini ketika kita mengatur data ke adaptor?
Milan Gajera
11

Coba ini. Jawabannya sangat terlambat. Tapi pasti membantu siapa pun di masa depan.

Setel Scrollview Anda ke NestedScrollView

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

Di Recyclerview Anda

recyclerView.setNestedScrollingEnabled(false); 
recyclerView.setHasFixedSize(false);
Ranjith Kumar
sumber
8
menggunakan RecyclerView di dalam NestedScrollView memanggil onBindView untuk setiap item dalam daftar meskipun item tersebut tidak terlihat. Ada solusi untuk masalah itu?
penumpang utama
8

UPDATE: jawaban ini sudah usang sekarang karena ada widget seperti NestedScrollView dan RecyclerView yang mendukung pengguliran bersarang.

Anda tidak boleh meletakkan tampilan yang dapat digulir di dalam tampilan yang dapat digulir lainnya!

saya sarankan Anda membuat tampilan pendaur ulang tata letak utama Anda dan menempatkan pandangan Anda sebagai item tampilan pendaur ulang.

lihat contoh ini yang menunjukkan cara menggunakan beberapa tampilan di dalam adaptor tampilan pendaur ulang. tautan ke contoh

EC84B4
sumber
saya punya halaman dengan lebih dari satu Pendaur Ulang , apakah ada cara lain untuk meyakinkan ini? beberapa hal seperti instagram atau komentar google play part yang memuat lebih banyak catatan ketika Anda mengklik lebih banyak komentar
Ashkan
menjadikannya recyclerView tunggal dan menempatkan pandangan Anda sebagai item untuk pendaur ulang itu
EC84B4
6
Itu omong kosong. Anda dapat memiliki RecyclerViews bersarang dengan baik.
IgorGanapolsky
Jawaban ini sekarang sudah usang. Kami memiliki hal-hal seperti NestedScrollView yang memungkinkan tampilan bersarang digulir. Nested RecyclerViews juga sekarang berfungsi.
Corey Horn
7

Jika Anda memasukkan ke RecyclerViewdalam NestedScrollViewdan mengaktifkan recyclerView.setNestedScrollingEnabled(false);, menggulir akan bekerja dengan baik .
Namun, ada masalah

RecyclerView jangan didaur ulang

Misalnya, RecyclerView(dalam NestedScrollViewatau ScrollView) Anda memiliki 100 item.
Saat Activitydiluncurkan, 100 item akan dibuat ( onCreateViewHolderdan onBindViewHolder100 item akan dipanggil bersamaan).
Contoh, untuk setiap item, Anda akan memuat gambar besar dari API => aktivitas yang dibuat -> 100 gambar akan dimuat.
Itu membuat Aktivitas lambat dan lambat.
Solusi yang memungkinkan :
- Berpikir untuk menggunakan RecyclerViewdengan beberapa tipe.

Namun, jika dalam kasus Anda, hanya ada beberapa item dalam RecyclerViewdan mendaur ulang atau tidak mendaur ulang tidak banyak mempengaruhi kinerja, Anda dapat menggunakan RecyclerViewdi dalam ScrollViewuntuk sederhana

Phan Van Linh
sumber
Tunjukkan bagaimana saya bisa membuat RecyclerView mendaur ulang bahkan di dalam ScollView? Terima kasih!
Pembohong
2
@ Liar, saat ini tidak ada cara untuk melakukan RecyclerViewdaur ulang setelah dimasukkan ke ScrollView. Jika Anda ingin mendaur ulang, pikirkan pendekatan lain (seperti menggunakan RecyclerView dengan beberapa tipe)
Phan Van Linh
4

Anda dapat menggunakan cara ini:

Tambahkan baris ini ke tampilan xml recyclerView Anda:

        android:nestedScrollingEnabled="false"

coba saja, recyclerview akan digulir dengan lancar dengan ketinggian yang fleksibel

Semoga ini bisa membantu.

iDeveloper
sumber
2

Tampaknya NestedScrollViewmemang memecahkan masalah. Saya sudah diuji menggunakan tata letak ini:

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

Dan itu bekerja tanpa masalah

royB
sumber
Bro, Masih Memiliki Masalah yang Sama Setelah diubah menjadi NestedScrollview dari Scrollview.
MohanRaj S
mmm ... bisakah Anda membagikan beberapa kode ... Saya mengalami nol masalah tetapi Anda tidak akan pernah tahu dengan masalah seperti ini
royB
bagaimana saya bisa membagikan kode saya melalui email atau melalui stack overflow
MohanRaj S
2
menggunakan kode ini panggilan diBindView untuk semua item dalam daftar meskipun item tersebut tidak terlihat dalam daftar. Ini mengalahkan tujuan recyclerview.
penumpang utama
2

Saya menggunakan CustomLayoutManager untuk menonaktifkan RecyclerView Scrolling. Juga jangan gunakan Recycler View sebagai WrapContent, gunakan sebagai 0dp, Berat = 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();
    }
}

Gunakan CustomLayoutManager di RecyclerView:

CustomLayoutManager mLayoutManager = new CustomLayoutManager(getBaseActivity(), CustomLayoutManager.VERTICAL, false);
        recyclerView.setLayoutManager(mLayoutManager);
        ((DefaultItemAnimator) recyclerView.getItemAnimator()).setSupportsChangeAnimations(false); 
        recyclerView.setAdapter(statsAdapter);

XML UI:

<?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
sumber
2

Saya mengalami masalah yang sama. Itu yang saya coba dan berhasil. Saya membagikan kode xml dan java saya. Semoga ini bisa membantu seseorang.

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

Berikut ini adalah kode java terkait. Itu bekerja seperti pesona.

LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setNestedScrollingEnabled(false);
Zeeshan Shabbir
sumber
Bagaimana dengan tata letak di bawah RecyclerView?
Tapa Hemat
itu juga berfungsi. Anda hanya perlu menggunakannya. NestedScrollViewalih-alih tampilan gulir
Zeeshan Shabbir
Ya, hanya NestedScrollView. Tetapi solusi dengan NestedScrollView + RecycleView memanggil populasi RecyclerView yang sangat lambat (saya pikir untuk perhitungan Y-posisi tampilan pertama setelah RecyclerView)
Tapa Save
1

Ini caranya:

recyclerView.setNestedScrollingEnabled(false);
Shylendra Madda
sumber
0

Sebenarnya tujuan utama dari RecyclerViewkompensasi adalah untuk ListViewdan ScrollView. Alih-alih melakukan apa yang sebenarnya Anda lakukan: Memiliki RecyclerViewdalam ScrollView, saya sarankan hanya memiliki RecyclerViewyang dapat menangani banyak jenis anak.

Samer
sumber
1
Ini akan berhasil hanya asalkan anak-anak Anda dapat dikumpulkan dikumpulkan begitu Anda menggulir mereka keluar dari pandangan. Jika Anda memiliki anak yang memiliki mapFragments atau streetview, itu tidak masuk akal karena mereka dipaksa untuk memuat ulang setiap kali mereka menggulir keluar dari recyclerview. Memasukkannya ke dalam scrollview dan kemudian menghasilkan recyclerview di bagian bawah lebih masuk akal.
Simon
1
@Simon ada setIsRecyclable () yang berguna di ViewHolder
ernazm
RecyclerView menggantikan ListView itu tidak dimaksudkan untuk menggantikan ScrollView.
cyroxis
Komentar ini layak lebih baik. Ini adalah solusi, dan bahkan lebih baik daripada yang diterima.
Kai Wang
0

Pertama, Anda harus menggunakan dan NestedScrollViewbukannya ScrollViewmeletakkan di RecyclerViewdalam NestedScrollView.

Gunakan kelas tata letak khusus untuk mengukur tinggi dan lebar layar:

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

Dan terapkan kode di bawah ini dalam aktivitas / fragmen 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
sumber
0

Jika RecyclerView hanya menampilkan satu baris di dalam ScrollView. Anda hanya perlu mengatur ketinggian baris Anda android:layout_height="wrap_content".

SANAT
sumber
0

Anda juga dapat mengesampingkan LinearLayoutManager untuk membuat roll recyclerview dengan lancar

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

Maaf terlambat ke pesta, tetapi sepertinya ada solusi lain yang bekerja sempurna untuk kasus yang Anda sebutkan.

Jika Anda menggunakan tampilan pendaur ulang di dalam tampilan pendaur ulang, tampaknya berfungsi dengan baik. Saya pribadi telah mencoba dan menggunakannya, dan tampaknya tidak memberikan kelambatan dan tidak ada dendeng sama sekali. Sekarang saya tidak yakin apakah ini adalah praktik yang baik atau tidak, tetapi menumpuk beberapa tampilan pendaur ulang, bahkan tampilan gulir bersarang melambat. Tapi ini sepertinya bekerja dengan baik. Ayo cobalah. Saya yakin bersarang akan baik-baik saja dengan ini.

Shailendra Pundhir
sumber
0

Pendekatan lain untuk mengatasi masalah ini adalah menggunakan ConstraintLayoutdi dalam ScrollView:

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

Tapi saya masih akan tetap pada androidx.core.widget.NestedScrollView pendekatan, yang diusulkan oleh Yang Peiyong .

soshial
sumber
-2

** Solusi yang berfungsi untuk saya.
Gunakan NestedScrollView dengan tinggi sebagai 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"

// Konten baris Anda ada di sini

maruti060385
sumber
Beberapa komentar pada jawaban yang sama mengatakan menonaktifkan pengguliran bersarang mengalahkan tujuan menggunakan RecyclerView, yaitu tidak akan mendaur ulang tampilan. Tidak yakin bagaimana mengonfirmasi itu.
jk7
1
Pengaturan nestedScrollingEnabled = "false" menyebabkan RecyclerView TIDAK mendaur ulang pandangannya, setidaknya di pengaturan saya (sebuah RecyclerView di dalam NestedScrollView). Dikonfirmasi dengan menambahkan RecyclerListener ke RecyclerView dan mengatur breakpoint di dalam metode onViewRecycled ().
jk7