我试图实现一个布局,其中包含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的最后一个元素。

我尝试过的事情:

在ScrollView里面的卡片视图(现在ScrollView包含RecyclerView) -可以看到卡片直到RecyclerView。 最初的想法是使用RecyclerView来实现这个ViewGroup而不是ScrollView,其中一个视图类型是CardView,但我得到了与ScrollView完全相同的结果。


当前回答

这个方法对我很有效

使用NestedScrollView与高度wrap_content,并为您的RecyclerView设置:

<androidx.recyclerview.widget.RecyclerView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:nestedScrollingEnabled="false"
    app:layoutManager="android.support.v7.widget.LinearLayoutManager" />

并设置视图持有者布局参数:

android:layout_width="match_parent"
android:layout_height="wrap_content"

其他回答

首先,你应该使用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!");
            }
        }
    });

在ScrollViews中放入RecyclerViews很好,只要它们不是自己滚动。在这种情况下,将其设置为固定高度是有意义的。

正确的解决方案是在RecyclerView高度上使用wrap_content,然后实现一个可以正确处理包装的自定义LinearLayoutManager。

复制这个LinearLayoutManager到你的项目:link

然后包装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,以编程方式膨胀子视图,并将它们添加到布局中。

我知道我迟到了,但即使谷歌已经在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>

解决方法是用RelativeLayout包装RecyclerView。不要问我是怎么找到这个变通办法的!!¯\_()_/¯

<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

下面是一个演示截图,展示了修复的操作:

新的Android支持库23.2解决了这个问题,你现在可以将wrap_content设置为RecyclerView的高度并正确工作。

Android支持库23.2

实际上,RecyclerView的主要目的是补偿ListView和ScrollView。而不是做你实际上在做的事情:在ScrollView中有一个RecyclerView,我建议只有一个RecyclerView可以处理许多类型的子。