RecyclerView всередині ScrollView не працює


276

Я намагаюся реалізувати макет, який містить RecyclerView і ScrollView в одному макеті.

Шаблон макета:

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

Проблеми: я можу прокручувати до останнього елемента ScrollView

Те, що я спробував:

  1. перегляд картки всередині ScrollView(тепер ScrollViewміститься RecyclerView) - може бачити карту доRecyclerView
  2. Початкова думка полягала в тому, щоб реалізувати це, viewGroupвикористовуючи RecyclerViewзамість того, ScrollViewде один із типів його переглядів є, CardViewале я отримав такі самі результати, як і дляScrollView

перевірка цього підходу: stackoverflow.com/a/21878703/684582
Alécio Carvalho

15
простим рішенням у багатьох із цих випадків є використання NestedScrollViewнатомість, оскільки він обробляє багато питань прокрутки
Річард Ле Месюр'є

Річард дав вам відповідь у лютому. Використовуйте NestedScrollViewзамість ScrollView. Саме це і є.
Майк М.

2
Це нічого не змінює для мене.
Люк Еллісон

2
Для подальшої довідки, якщо у когось виникають подібні проблеми лише на пристроях зефір / нуга (API 23, 24), перевірте моє вирішення за адресою stackoverflow.com/a/38995399/132121
Hossain Khan

Відповіді:


653

використовувати NestedScrollViewзамістьScrollView

Перейди через NestedScrollView довідковий документ для отримання додаткової інформації.

і додайте recyclerView.setNestedScrollingEnabled(false);до свогоRecyclerView


24
Працює з: android.support.v4.widget.NestedScrollView
Cocorico

7
тримайте android:layout_height="wrap_content"за макет завищений для ViewHolder
Сарат Бабу

4
У складній компоновці NestedScrollViewдля мене відстає, на відміну від ScrollView. Пошук рішення без використанняNestedScrollView
Роман Самойленко

7
Також ви можете додати android: nestedScrollingEnabled = "false" до XML замість recilerView.setNestedScrollingEnabled (false);
костябакай

2
Це працювало для мене, але майте на увазі, що елементи всередині recilerView не піддаються переробці.
Мостафа Аріан Неджад

102

Я знаю, що я запізнююсь на цю гру, але проблема все ще існує навіть після того, як Google виправив на android.support.v7.widget.RecyclerView

Проблема, яку я отримую зараз, полягає RecyclerViewв тому, що layout_height=wrap_contentне приймають висоту всіх виданих елементів всередині, ScrollViewщо відбувається лише у версіях Marshmallow та Nougat + (API 23, 24, 25).
(UPDATE: Заміна ScrollViewз android.support.v4.widget.NestedScrollViewроботою на всі версії я як - то пропустив тестування. Загальноприйнята рішення Додано це в моєму GitHub проект в якості демо.) .

Спробувавши різні речі, я знайшов рішення, яке вирішує цю проблему.

Ось моя структура компонування в двох словах:

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

Обійти це згортають RecyclerViewз RelativeLayout. Не запитуйте мене, як я знайшов це вирішення !!!¯\_(ツ)_/¯

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

Повний приклад доступний у проекті GitHub - https://github.com/amardeshbd/android-recycler-view-wrap-content

Ось демонстраційна демонстрація екрана, що показує виправлення в дії:

Екранна трансляція


6
Спасибі людино .. її допомога багато .. але прокрутка стає важкою після вашого рішення, тому я встановив recilerview.setNestedScrollingEnabled (помилково); і тепер його робота як шарм.
Сагар Чавада

4
Чи цей метод переробляє перегляди? якщо у нас є сотні об'єктів для переробки. Це злом не рішення.
androidXP

1
Так, @androidXP має рацію, цей хак не є рішенням для довгого списку. Мій випадок використання був фіксованим елементом у переліку списку, який був менше 10. А щодо того, як я знайшов інший спосіб вирішення, я пробував випадкові речі, це був один із них :-)
Хоссайн Хан

Ідеально вирішив мою проблему в області адероїдної зефіру та верхівки. ;)
Амір Зіараті

2
якщо я використовую це рішення, тоді onBindView викликає всі елементи списку, що не є випадком використання reciklierview.
thearkarkas

54

Хоча рекомендація, що

ніколи не слід розміщувати прокручуваний вид всередині іншого виду, що прокручується

Це обгрунтована порада, однак якщо ви встановите фіксовану висоту на перегляді рециркулятора, він повинен працювати добре.

Якщо вам відома висота макета елемента адаптера, ви можете просто обчислити висоту RecyclerView.

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

10
Як отримати adapterItemSize в перегляді будь-якої ідеї?
Крутик

1
Це працює як шарм! Невелике виправлення має бути: int viewHeight = adapterItemSize * adapterData.size (); recilerView.getLayoutParams (). висота = viewHeight;
Вайбхав Яні

3
Як дізнатися adapterItemSize?
droidster.me

2
@JoakimEngstrom Що таке adapterItemSizeзмінна?
ІгорГанапольський

1
Уникайте подання рециркулятора всередині подання прокрутки, оскільки подання прокрутки надає дитині нескінченний простір. Це спричиняє вигляд рециркулятора, який має висоту wrap_content для вимірювання нескінченно у вертикальному напрямку (до останнього пункту подання рециркулятора). Замість використання перегляду рециркулятора всередині подання прокрутки використовуйте лише перегляд рециркулятора з різними типами елементів. З цією реалізацією діти прокрутки будуть поводитись як тип перегляду. Обробляйте ці типи зображень усередині подання утилізатора.
ахішеш

28

У випадку, якщо встановлення фіксованої висоти для RecyclerView не працювало для когось (як я), ось що я додав до рішення з фіксованою висотою:

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
Дійсно, це спрацювало добре для мене. Маючи це на місці, я зміг перемістити подання прокрутки вгору та вниз, і коли я вибираю перегляд рециркулятора для прокрутки, він має пріоритет над поданням прокрутки. Дякую за підказку
PGMacDesign

1
він працював і для мене, просто не забудьте скористатися getParent для прямого довідника прокрутки
BigBen3216

Цей метод працює і на цианогенних розподілах. Рішення з фіксованою висотою на ціаногенних модах працює, але лише в тому випадку, якщо фіксована висота є абсолютною висотою всіх елементів у списку, що в першу чергу не відповідає точці використання рециркулятора. Оголошено.
HappyKatz

Мені також знадобився recilerView.setNestedScrollingEnabled (помилково);
Аналізатор

25

Нова бібліотека підтримки Android 23.2 вирішує цю проблему, тепер ви можете встановити wrap_content як висоту своєї RecyclerViewта працює правильно.

Бібліотека підтримки Android 23.2


теж не налетить належним чином (23.4.0)
behelit

1
@behelit 23.4.0 є деякі проблеми code.google.com/p/android/issues/detail?id=210085#makechanges , використовуйте 23.2.1 замість
Самед

1
навіть не летить належним чином 25.0.1
maruti060385

15

RecyclerViews добре розміщувати в ScrollView, якщо вони самі не прокручуються. У цьому випадку має сенс зробити його фіксованою висотою.

Правильне рішення - використовувати wrap_content на висоті RecyclerView, а потім реалізація користувальницького LinearLayoutManager, який може правильно обробляти обгортання.

Скопіюйте цей LinearLayoutManager у свій проект: https://github.com/serso/android-linear-layout-manager/blob/master/lib/src/main/java/org/solovyev/android/views/llm/LinearLayoutManager.java

Потім оберніть RecyclerView:

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

І налаштуйте так:

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

Редагувати: Це може спричинити ускладнення при прокручуванні, оскільки RecyclerView може вкрасти сенсорні події ScrollView. Моє рішення полягало лише в тому, щоб вирвати RecyclerView у всіх і перейти з LinearLayout, програмно надути підгляди та додати їх до макета.


4
Ви не можете викликати setNestedScrollingEnabled (помилково) на перегляді рециркулятора?
Ешаан

14

Тому що ScrollViewви можете використовувати fillViewport=trueта робити, layout_height="match_parent"як показано нижче, і помістити всередину перегляд:

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

Не потрібно більше коригування висоти за допомогою коду.


Випробувано добре, використовуючи v23.2.1. Використовував його для додавання макета вище recilerview.
victontan520

тільки RecyclerView прокрутки і ScrollView НЕ прокрутка
Самед

13

Розрахувати RecyclerViewвисоту вручну - це не добре, краще використовувати користувальницькийLayoutManager .

Причина даного питання є будь-яким видом , який має свій сувій ( ListView, GridView, RecyclerView) не вирахує його висоту , коли додати як дитина в інший точці зору є сувій. Тож переважаючий їїonMeasure методу вирішить проблему.

Будь ласка, замініть менеджер макетів за замовчуванням на нижченаведене:

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

Як я можу викликати цей клас, коли ми встановлюємо дані на адаптер?
Мілан Гаджера

11

Спробуйте це. Дуже пізня відповідь. Але, безумовно, допоможіть будь-кому в майбутньому.

Встановіть для перегляду прокрутки значення NestedScrollView

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

У вашому Recyclerview

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

8
використання RecyclerView всередині NestedScrollView викликає onBindView для кожного елемента в списку, навіть якщо елемент не відображається. Будь-яке рішення цієї проблеми?
theparkpassenger

8

ОНОВЛЕННЯ: ця відповідь застаріла, оскільки є віджети, такі як NestedScrollView і RecyclerView, які підтримують вкладені прокрутки.

ніколи не слід ставити прокручуваний вид всередині іншого виду, що прокручується!

Я пропоную вам створити основний вигляд рециркулятора макета і розмістити свої погляди як елементи перегляду утилізатора.

Погляньте на цей приклад, в якому показано, як використовувати декілька подань усередині адаптера подання переробника. посилання на приклад


У мене є сторінка з кількома переробниками , чи є інший спосіб переконати це? щось таке, як коментар у програмі instagram або google play, який завантажує більше записів, натискаючи на більше коментаря
Ashkan

перетворіть його в єдиний переробникПерегляньте і поставте свої погляди як предмети для цього переробника
EC84B4

6
Це нісенітниця. Ви можете просто вкласти RecyclerViews.
ІгорГанапольський

Ця відповідь застаріла. У нас є такі речі, як NestedScrollView, які дозволяють створити вкладені представлення для прокрутки. Вкладені RecyclerViews також зараз працюють.
Corey Horn

7

Якщо помістити RecyclerViewвсередину NestedScrollViewта включити recyclerView.setNestedScrollingEnabled(false);, прокрутка буде добре працювати .
Однак є проблема

RecyclerView не переробляти

Наприклад, у вас RecyclerView(всередині NestedScrollViewабо ScrollView) є 100 предметів.
При Activityзапуску буде створено 100 елемент ( onCreateViewHolderі onBindViewHolder100 елементів буде викликано одночасно).
Наприклад, для кожного елемента ви завантажите велике зображення з API => створеної діяльності -> 100 зображень завантажуватимуться.
Це робить початкову повільність активності та відставання.
Можливе рішення :
- Думаючи про використанняRecyclerView з декількома типами.

Однак, якщо у вашому випадку, є тільки кілька пункт в RecyclerViewі рециркуляційних або НЕ Рецикл не впливає на продуктивність багато, ви можете використовувати RecyclerViewвсередині ScrollViewдля простого


Покажіть, як я можу змусити RecyclerView переробити навіть усередині ScollView? Дякую!
брехун

2
@Liar, в даний час немає способу зробити RecyclerViewпереробку після того, як поставити її ScrollView. Якщо ви хочете переробити, подумайте про інший підхід (як, наприклад, використання RecyclerView з декількома типами)
Phan Van Linh

4

Ви можете використовувати такий спосіб:

Додайте цей рядок до перегляду reciklierView xml:

        android:nestedScrollingEnabled="false"

спробуйте, reciklierview буде плавно прокручуватися з гнучкою висотою

сподіваюся, що це допомогло.


2

Здається, NestedScrollViewце все-таки вирішує проблему. Я протестував за допомогою цього макета:

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

І це працює без проблем


Брат, все-таки мають таку ж проблему після зміни в NestedScrollview з Scrollview.
MohanRaj S

ммм ... Ви можете поділитися яким-небудь кодом ... У мене нульові проблеми, але ви ніколи не можете знати з подібними питаннями
royB

Як я можу поділитися кодом електронною поштою або через переповнення стека
MohanRaj S

2
за допомогою цього коду викликає onBindView для всіх елементів у списку, навіть якщо ці елементи не відображаються у списку. Це перемагає мету перегляду рециркулятора.
theparkpassenger

2

Я використовував CustomLayoutManager, щоб відключити прокрутку RecyclerView. Також не використовуйте перегляд Recycler як WrapContent, використовуйте його як 0dp, вага = 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();
    }
}

Використовуйте CustomLayoutManager в RecyclerView:

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

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

У мене була така ж проблема. Ось що я спробував, і це працює. Я ділюсь своїм XML та Java-кодом. Сподіваюся, що це комусь допоможе.

Ось 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 >

Ось відповідний код Java. Це працює як шарм.

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

А як щодо макетів нижче RecyclerView?
Tapa Save

це також працює. Вам просто потрібно скористатися. NestedScrollViewзамість подання прокрутки
Зеешан Шаббір

Так, лише NestedScrollView. Але рішення з NestedScrollView + RecycleView викликають дуже повільні групи RecyclerView (я думаю, для обчислення Y-позиції першого перегляду після RecyclerView)
Tapa Save


0

Насправді основною метою діяльності RecyclerViewє компенсація ListViewта ScrollView. Замість того, щоб робити те, що ви насправді робите: маючи RecyclerViewв собі ScrollView, я б запропонував мати лише RecyclerViewте, що може працювати з багатьма типами дітей.


1
Це спрацювало б лише за умови, що ваших дітей можна збирати вбрані, як тільки ви прокрутите їх з поля зору. Якщо у вас є діти, які представляють mapFragments або streetviews, це не має сенсу, оскільки вони змушені перезавантажувати кожного разу, коли вони прокручують перегляд reciklier. Вбудовувати їх у перегляд прокрутки, а потім генерувати перегляд рециркулятора внизу має більше сенсу.
Саймон

1
@Simon є зручний setIsRecyclable () у ViewHolder
ernazm

RecyclerView замінює ListView, він не призначений для заміни ScrollView.
цироксис

Цей коментар заслуговує кращого. Це рішення, і навіть краще, ніж прийняте.
Кай Ван

0

Спочатку слід використовувати NestedScrollViewзамість цього ScrollViewі помістити RecyclerViewвсерединуNestedScrollView .

Використовуйте клас власної компонування для вимірювання висоти та ширини екрана:

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

І реалізуйте код нижче в активності / фрагменті 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

Якщо RecyclerView показує лише один рядок всередині ScrollView. Вам просто потрібно встановити висоту рядка android:layout_height="wrap_content".


0

ви також можете перекрити LinearLayoutManager, щоб зробити перегляд рециркулятора плавно

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

0

Вибачте, що запізнилися на вечірку, але, схоже, є ще одне рішення, яке ідеально підходить для випадку, про який ви згадали.

Якщо ви використовуєте вигляд рециркулятора всередині подання утилізатора, він, здається, працює чудово. Я особисто пробував і використовував це, і, здається, він не дає ніякої повільності і ніякої ривкості. Зараз я не впевнений, це хороша практика чи ні, але вкладення кількох переглядів утилізатора, навіть вкладений перегляд прокрутки сповільнюється. Але це, здається, прекрасно працює. Спробуйте, спробуйте. Я впевнений, що з цим гніздування буде прекрасно.


0

Іншим підходом до вирішення проблеми є використання ConstraintLayoutвсередині ScrollView:

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

Але я б все-таки дотримувався androidx.core.widget.NestedScrollView підходу, запропонованого Ян Пейонгом .


-2

** Рішення, яке працювало для мене
Використовуйте NestedScrollView з висотою як 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"

// Ваш вміст рядка йде сюди


Деякі коментарі до подібної відповіді кажуть, що відключення вкладеної прокрутки перемагає мету використання RecyclerView, тобто те, що воно не переробляє перегляди. Не знаєте, як це підтвердити.
jk7

1
Встановлення nestedScrollingEnabled = "false" призводить до того, що RecyclerView НЕ переробляє свої погляди, принаймні в моїй установці (RecyclerView всередині NestedScrollView). Підтверджується, додаючи RecyclerListener до RecyclerView і встановлюючи точку перелому всередині його методу onViewRecycled ().
jk7
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.