ScrollView 내부의 RecyclerView가 작동하지 않습니다


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그것은 스크롤 문제를 많이 처리로 대신
— 리처드 르 Mesurier

리차드는 2 월에 답을 주었다. NestedScrollView대신에을 사용하십시오 ScrollView. 그것이 바로 그 목적입니다.
— Mike M.

2
나를 위해 아무것도 변경하지 않습니다.
— Luke Allison

2
나중에 참조하기 위해 marshmallow / nougat (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
— Roman Samoylenko

7
또한 recyclerView.setNestedScrollingEnabled (false) 대신 android : nestedScrollingEnabled = "false"를 XML에 추가 할 수 있습니다.
— kostyabakay

2
그것은 나를 위해 일했지만 recyclerView 내부의 항목은 재활용되지 않는다는 것을 명심하십시오.
— Mostafa Arian Nejad

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

다음은 수정 사항을 보여주는 데모 스크린 캐스트입니다.

스크린 캐스트


6
고마워요 .. 그것의 도움이 많이 ..하지만 솔루션 후 스크롤이 어려워서 recyclerview.setNestedScrollingEnabled (false); 그리고 지금은 매력처럼 작동합니다.
— Sagar Chavada

4
이 방법은 뷰를 재활용합니까? 재활용 할 수백 개의 물체가 있다면 이것은 해킹이 아닙니다.
— androidXP 2016 년

1
예, @androidXP가 옳습니다.이 해킹은 긴 목록에 대한 해결책이 아닙니다. 내 유스 케이스는이 :-) 그들 중 하나였다 적은 10보다 내가 다른 해결 방법을 발견하는 방법에 관해서는, 나는 임의의 일을 시도 하였다 목록보기에서 항목을 수정되었습니다
— 의 Hossain 칸

adnroid marshmallow와 upper에서 내 문제를 해결했습니다. ;)
— Amir Ziarati

2
이 솔루션을 사용하면 onBindView가 목록의 모든 항목에 대해 호출되며 이는 recyclerview의 사용 사례가 아닙니다.
— thedarkpassenger 2016 년

54

추천하지만

스크롤 가능한 뷰를 다른 스크롤 가능한 뷰 안에 넣지 마십시오.

올바른 조언이지만, 리사이클 뷰에서 고정 높이를 설정하면 제대로 작동합니다.

어댑터 항목 레이아웃의 높이를 알고 있으면 RecyclerView의 높이를 계산할 수 있습니다.

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

10
recyclerview에서 adapterItemSize를 얻는 방법 아이디어가 있습니까?
— Krutik

1
그것은 매력처럼 작동합니다! 약간만 수정하면됩니다 : int viewHeight = adapterItemSize * adapterData.size (); recyclerView.getLayoutParams (). height = viewHeight;
— Vaibhav Jani

3
adapterItemSize를 찾는 방법은 무엇입니까?
— droidster.me

2
@JoakimEngstrom adapterItemSize변수 는 무엇입니까 ?
— IgorGanapolsky

1
스크롤 뷰는 자식에 무한 공간을 제공하므로 스크롤 뷰 내부에서 리사이클 뷰를 피하십시오. 이렇게하면 세로 방향으로 무한히 측정 할 수있는 높이로 wrap_content가있는 리사이클 러보기가 발생합니다 (리사이클 러보기의 마지막 항목까지). 스크롤보기에서 재생기보기를 사용하는 대신 다른 항목 유형의 재생기보기 만 사용하십시오. 이 구현에서 스크롤보기의 하위는보기 유형으로 작동합니다. 리사이클 뷰 내에서 이러한 뷰 유형을 처리하십시오.
— abhishesh

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
실제로 이것은 나를 위해 잘 작동했습니다. 이를 사용하면 스크롤보기를 위아래로 움직일 수 있었고 스크롤을 위해 recyclerview를 선택하면 스크롤보기보다 우선합니다. 팁 주셔서 감사합니다
— PGMacDesign 2016 년

1
그것은 나를 위해 일했다. 단지
— 스크롤 뷰

이 방법은 cyanogenmod 분포에서도 작동합니다. cyanogenmod의 고정 높이 솔루션은 작동하지만 고정 높이가 목록에있는 모든 항목의 절대 높이 인 경우에만 우선적으로 recyclerview를 사용하는 시점을 무시합니다. 공감.
— HappyKatz

또한 recyclerView.setNestedScrollingEnabled (false)가 필요했습니다.
— Analizer


15

RecyclerView는 스스로 스크롤하지 않는 한 ScrollView에 넣는 것이 좋습니다. 이 경우 고정 높이로 만드는 것이 좋습니다.

올바른 해결책은 wrap_contentRecyclerView 높이 에서 사용 하고 래핑을 올바르게 처리 할 수있는 사용자 정의 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을 사용하여 정상적으로 작동하는지 테스트했습니다. recyclerview 위에 레이아웃을 추가하는 데 사용했습니다.
— winsontan520

RecyclerView 스크롤과 ScrollView 스크롤이 안됨
— Samad

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
NestedScrollView 내에서 RecyclerView 사용 항목이 보이지 않아도 목록의 모든 항목에 대해 onBindView를 호출합니다. 그 문제에 대한 해결책이 있습니까?
— thedarkpassenger 2016 년

8

업데이트 : 중첩 스크롤을 지원하는 NestedScrollView 및 RecyclerView와 같은 위젯이 있으므로이 답변의 날짜가 만료되었습니다.

스크롤 가능한 뷰를 다른 스크롤 가능한 뷰 안에 넣지 마십시오!

기본 레이아웃 재활용 뷰를 만들고 재활용 뷰의 항목으로 뷰를 배치하는 것이 좋습니다.

이 예제를 살펴보면 리사이클 뷰 어댑터에서 여러 뷰를 사용하는 방법을 보여줍니다. 예제로 연결


하나 이상의 Recycler 가있는 페이지 가 있는데 이것을 설득하는 다른 방법이 있습니까? 더 많은 댓글 을 클릭하면 더 많은 정보를로드하는 인스 타 그램 또는 구글 플레이 부분 댓글 과 같은 것
— Ashkan

하나의 리사이클
— 러로 만들고

6
말도 안 돼요 중첩 된 RecyclerViews를 잘 가질 수 있습니다.
— IgorGanapolsky

이 답변은 이제 구식입니다. 중첩 스크롤 가능 뷰를 허용하는 NestedScrollView와 같은 것들이 있습니다. Nested RecyclerViews도 작동합니다.
— Corey Horn

7

당신이 넣으면 RecyclerView내부 NestedScrollView및 활성화 recyclerView.setNestedScrollingEnabled(false);, 스크롤을 잘 작동 .
그러나 문제가 있습니다

RecyclerView 재활용하지 마십시오

예를 들어, RecyclerView( NestedScrollView또는 내부 ScrollView)에 100 개의 항목이 있습니다.
때 Activity출시, 100 항목이 생성됩니다 ( onCreateViewHolder및 onBindViewHolder100 항목의 같은 시간에 호출됩니다).
예를 들어, 각 항목에 대해 API => 활동 생성-> 100 이미지에서 큰 이미지를로드합니다.
시작 활동이 느리고 지연됩니다.
가능한 해결책 :
- RecyclerView여러 유형을 사용 하는 것에 대해 생각 합니다.

그러나 귀하의 경우에 품목이 몇 개 RecyclerView있고 재활용 또는 재활용 하지 않는 것이 성능에 큰 영향을 미치지 않는 경우 RecyclerView내부 ScrollView를 간단한 용도로 사용할 수 있습니다


ScollView 내부에서도 RecyclerView를 재활용 할 수있는 방법을 보여주십시오. 감사!
— Liar

2
@Liar, 현재에 RecyclerView넣은 후 재활용 할 수있는 방법이 없습니다 ScrollView. 재활용을 원한다면 다른 접근 방식을 생각하십시오 (여러 유형의 RecyclerView 사용)
— Phan Van Linh

4

이 방법으로 사용할 수 있습니다 :

이 라인을 recyclerView xml 뷰에 추가하십시오.

        android:nestedScrollingEnabled="false"

시도해보십시오 .recyclerview는 유연한 높이로 부드럽게 스크롤됩니다.

이것이 도움이 되었기를 바랍니다.


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>

그리고 문제없이 작동합니다


Bro, Scrollview에서 NestedScrollview로 변경 한 후에도 여전히 동일한 문제가 있습니다.
— MohanRaj S

mmm ... 코드를 공유 할 수 있습니까? 제로 문제가 없지만 이런 종류의 문제를 알 수는 없습니다
— royB

이메일이나 스택 오버플로를 통해 코드를 공유하는 방법
— MohanRaj S

2
이 코드를 사용하면 목록에 해당 항목이 표시되지 않더라도 목록의 모든 항목에 대해 onBindView를 호출합니다. 이것은 recyclerview의 목적을 무효화합니다.
— thedarkpassenger 2016 년

2

CustomLayoutManager를 사용하여 RecyclerView Scrolling을 비활성화했습니다. 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();
    }
}

RecyclerView에서 CustomLayoutManager를 사용하십시오.

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 아래의 레이아웃은 어떻습니까?
— Tapa 저장

잘 작동합니다. 사용하면됩니다. NestedScrollView스크롤보기 대신
— Zeeshan Shabbir

예, NestedScrollView 만 있습니다. 그러나 NestedScrollView + RecycleView가있는 솔루션은 매우 느린 RecyclerView 모집단을 호출합니다 (RecyclerView 이후 첫 번째보기의 Y 위치 계산을 생각합니다)
— Tapa Save


0

실제로의 주요 목적은 RecyclerView보상하는 것입니다 ListView및 ScrollView. 에서 실제로하는 일을하는 대신 : RecyclerViewin a 을 사용하면 많은 유형의 어린이를 처리 할 수 ScrollView있는 RecyclerView것을 사용 하는 것이 좋습니다 .


1
이것은 아이들이 시야 밖으로 스크롤하자마자 가비지 수집 될 수있는 경우에만 작동합니다. mapFragments 또는 streetviews 인 자식이있는 경우 Recyclerview를 스크롤 할 때마다 다시로드해야하므로 의미가 없습니다. 스크롤보기에 포함시킨 다음 하단에 리사이클 러보기를 생성하는 것이 더 합리적입니다.
— 시몬

1
편리한 ViewHolder에 setIsRecyclable () 거기 @ 사이먼
— ernazm

RecyclerView는 ListView를 대체하지만 ScrollView를 대체하지는 않습니다.
— cyroxis

이 의견은 더 가치가 있습니다. 그것은 해결책이며, 허용되는 것보다 훨씬 좋습니다.
— Kai Wang

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 Yang Peiyong이 제안한 접근 방식을 고수 할 것 입니다.


-2

** 나를 위해 일한 솔루션
wrap_content로 높이가 NestedScrollView를 사용하십시오.

<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"를 설정하면 적어도 내 설정 (NestedScrollView 내부의 RecyclerView)에서 RecyclerView가 뷰를 재활용하지 않습니다. RecyclerListener를 RecyclerView에 추가하고 onViewRecycled () 메서드 내에 중단 점을 설정하여 확인합니다.
— jk7
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.