RecyclerView внутри ScrollView не работает


278

Я пытаюсь реализовать макет, который содержит 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
Хоссейн Хан

Ответы:


654

использовать 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 вместо recyclerView.setNestedScrollingEnabled (false);
Костябакай

2
Это сработало для меня, но имейте в виду, что элементы в recyclerView не перерабатываются.
Мостафа Ариан Неджад

102

Я знаю, что опоздал в игре, но проблема все еще существует даже после того, как Google исправил android.support.v7.widget.RecyclerView

Вопрос я получаю сейчас RecyclerViewс layout_height=wrap_contentне принимая высоту всего выпуска предметов внутри , ScrollViewчто только происходит на зефира и нуги + (API 23, 24, 25) версий.
(ОБНОВЛЕНИЕ: замена 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

Вот демонстрационный скриншот, показывающий исправление в действии:

Screencast


6
Спасибо, человек ... это очень помогает ... но после твоего решения становится трудно прокручивать, поэтому я установил recyclerview.setNestedScrollingEnabled (false); и теперь его работа, как шарм.
Сагар Чавада

4
Этот метод перерабатывает представления? если у нас есть около сотни объектов для переработки. Это взломать, а не решение.
androidXP

1
Да, @androidXP прав, этот взлом не является решением для длинного списка. Мой вариант использования был фиксированным элементом в виде списка, который был меньше 10. И что касается того, как я нашел другой обходной путь, я пробовал случайные вещи, это был один из них :-)
Хоссейн Хан

perfect.solved моя проблема в зефире adnroid и верхнем. ;)
Амир Зиарати

2
если я использую это решение, то вызывается onBindView для всех элементов в списке, который не является случаем повторного просмотра.
thedarkpassenger

54

Хотя рекомендация

Вы никогда не должны помещать прокручиваемое представление в другое прокручиваемое представление

Это разумный совет, однако, если вы установите фиксированную высоту в окне рециркуляции, она должна работать нормально.

Если вы знаете высоту расположения элементов адаптера, вы можете просто рассчитать высоту RecyclerView.

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

10
Как получить adapterItemSize в утилитаре просмотра какой-либо идеи?
Крутик

1
Работает как часы! Небольшое исправление должно быть: int viewHeight = adapterItemSize * adapterData.size (); recyclerView.getLayoutParams (). height = 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

Этот метод работает и для распределения CyanogenMod. Решение с фиксированной высотой в cyanogenmod работает, но только в том случае, если фиксированная высота - это абсолютная высота всех элементов в списке, которая в первую очередь не соответствует точке зрения переработчика. Upvoted.
HappyKatz

Мне также нужно было recyclerView.setNestedScrollingEnabled (false);
Анализатор

25

Новая библиотека Android Поддержка 23.2 решает эту проблему, теперь вы можете установить wrap_content как высота вашего RecyclerViewи работает правильно.

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


тоже не швыряет (23.4.0)
бехелит

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 хорошо разместить в ScrollViews, если они не прокручивают сами себя. В этом случае имеет смысл установить фиксированную высоту.

Правильное решение заключается в использовании 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 (false) в представлении реселлера?
Eshaan

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. Использовал его для добавления макета выше.
winsontan520

только прокрутка 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

Попробуй это. Очень поздний ответ. Но обязательно помогите кому-нибудь в будущем.

Установите для Scrollview значение 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 для каждого элемента в списке, даже если элемент не отображается. Любое решение для этой проблемы?
thedarkpassenger

8

ОБНОВЛЕНИЕ: этот ответ устарел, так как есть виджеты, такие как NestedScrollView и RecyclerView, которые поддерживают вложенную прокрутку.

Вы никогда не должны помещать прокручиваемое представление в другое прокручиваемое представление!

Я предлагаю вам сделать ваш основной вид макета переработчиком и представить ваши взгляды как элементы представления переработчика.

взгляните на этот пример, который показывает, как использовать несколько представлений внутри адаптера представления переработчика. ссылка на пример


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

сделайте его одним переработчикомПросмотритесь и поместите ваши представления как элементы для этого переработчика
EC84B4

6
Это чепуха. Вы можете иметь вложенные RecyclerViews просто отлично.
Игорь Ганапольский

Этот ответ сейчас устарел. У нас есть такие вещи, как NestedScrollView, которые позволяют использовать вложенные прокручиваемые представления. Вложенные RecyclerViews также теперь работают.
Кори Хорн

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

Вы можете использовать этот способ либо:

Добавьте эту строку в ваше представление recyclerView xml:

        android:nestedScrollingEnabled="false"

попробуйте, реселлер-просмотр будет плавно прокручиваться с гибкой высотой

надеюсь, это помогло.


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>

И это работает без проблем


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

ммм ... не могли бы вы поделиться кодом ... у меня ноль проблем, но вы никогда не сможете узнать с такими проблемами
royB

Как я могу поделиться своим кодом по электронной почте или через переполнение стека
MohanRaj S

2
использование этого кода вызывает onBindView для всех элементов в списке, даже если эти элементы не отображаются в списке. Это побеждает цель повторной проверки.
thedarkpassenger

2

Я использовал CustomLayoutManager, чтобы отключить прокрутку RecyclerView. Также не используйте Recycler View в качестве WrapContent, используйте его как 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();
    }
}

Используйте CustomLayoutManager в RecyclerView:

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

UI XML:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/background_main"
    android:fillViewport="false">


    <LinearLayout
        android:id="@+id/contParentLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <edu.aku.family_hifazat.libraries.mpchart.charts.PieChart
                android:id="@+id/chart1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="@dimen/x20dp"
                android:minHeight="@dimen/x300dp">

            </edu.aku.family_hifazat.libraries.mpchart.charts.PieChart>


        </FrameLayout>

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1">


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


    </LinearLayout>


</ScrollView>

2

У меня была такая же проблема. Это то, что я пытался, и это работает. Я делюсь своим 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?
Тапа Сохранить

это тоже работает. Вам просто нужно использовать. NestedScrollViewвместо просмотра прокрутки
Зеешан Шаббир

Да, только NestedScrollView. Но решение с NestedScrollView + RecycleView вызывает очень медленные популяции RecyclerView (я думаю, для расчета Y-позиции первого вида после RecyclerView)
Tapa Save


0

На самом деле основная цель состоит в том RecyclerView, чтобы компенсировать ListViewи ScrollView. Вместо того, чтобы делать то, что вы на самом деле делаете: имея RecyclerViewв ScrollView, я бы предложил иметь только тот, RecyclerViewкоторый может обрабатывать многие типы детей.


1
Это сработает только при условии, что ваши дети могут быть собраны, как только вы прокрутите их из поля зрения. Если у вас есть дочерние объекты, которые являются фрагментами карты или улицами, это не имеет смысла, так как они вынуждены перезагружаться каждый раз, когда они прокручивают обзор переработчика. Включение их в представление прокрутки, а затем создание представления переработчика в нижней части имеет больше смысла.
Саймон

1
@ Симон, есть удобный setIsRecyclable () во ViewHolder
ernazm

RecyclerView заменяет ListView, он не предназначен для замены ScrollView.
Cyroxis

Этот комментарий заслуживает лучшего. Это решение, и даже лучше, чем принятое.
Кай Ван

0

Сначала вы должны использовать NestedScrollViewвместо ScrollViewи положить RecyclerViewвнутрьNestedScrollView .

Используйте класс Custom layout для измерения высоты и ширины экрана:

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.