ChatGPT解决这个技术问题 Extra ChatGPT

ScrollView 中的 RecyclerView 不起作用

我正在尝试在同一布局中实现包含 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 而不是 ScrollView 来实现这个 viewGroup,其中一个视图类型是 CardView 但我得到的结果与滚动视图

在许多情况下,一个简单的解决方案是改用 NestedScrollView,因为它可以处理很多滚动问题
理查德在二月份给了你答案。使用 NestedScrollView 而不是 ScrollView。这正是它的用途。
对我来说没有任何改变。
以供将来参考,如果有人在棉花糖/牛轧糖(API 23、24)设备上遇到类似问题,请在 stackoverflow.com/a/38995399/132121 查看我的解决方法

c
cascal

使用 NestedScrollView 而不是 ScrollView

请通过 NestedScrollView reference document 了解更多信息。

并将 recyclerView.setNestedScrollingEnabled(false); 添加到您的 RecyclerView


适用于:android.support.v4.widget.NestedScrollView
为 ViewHolder 膨胀的布局保留 android:layout_height="wrap_content"
在复杂的布局中,NestedScrollView 对我来说滞后,与 ScrollView 不同。不使用 NestedScrollView 搜索解决方案
您也可以将 android:nestedScrollingEnabled="false" 添加到 XML 而不是 recyclerView.setNestedScrollingEnabled(false);
它对我有用,但请记住,recyclerView 中的项目没有被回收。
M
Mehdi Dehghani

我知道我迟到了,但即使谷歌修复了 android.support.v7.widget.RecyclerView,问题仍然存在

我现在遇到的问题是 RecyclerView,其中 layout_height=wrap_content 没有计算 ScrollView 内所有项目的高度,发生在 Marshmallow 和 Nougat+(API 23、24、25)版本上。< br> (更新:用 android.support.v4.widget.NestedScrollView 替换 ScrollView 适用于所有版本。我不知何故错过了测试 accepted solution。将此作为演示添加到我的 github 项目中。)

在尝试了不同的事情之后,我找到了解决此问题的解决方法。

简而言之,这是我的布局结构:

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

解决方法是将 RecyclerViewRelativeLayout 包装起来。不要问我是怎么找到这个解决方法的!!! ¯\_(ツ)_/¯

<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 上提供了完整示例

这是一个演示截屏视频,显示了正在执行的修复:

https://github.com/amardeshbd/android-recycler-view-wrap-content/raw/master/web-resources/RecyclerView-wrap_content-demo-screen-cash.gif


谢谢伙计..它的帮助很大..但是在您的解决方案之后滚动变得很困难所以我设置了 recyclerview.setNestedScrollingEnabled(false);现在它的工作就像一个魅力。
这种方法是回收视图吗?如果我们有大约数百个要回收的对象。这是hack而不是解决方案。
是的,@androidXP 是对的,这个 hack 不是一长串的解决方案。我的用例是列表视图中小于 10 个的固定项目。至于我如何找到其他解决方法,我正在尝试随机的东西,这就是其中之一:-)
如果我使用这个解决方案,那么会为列表中的所有项目调用 onBindView,这不是 recyclerview 的用例。
你是天才!惊人的。
O
OWADVL

虽然建议

你不应该把一个可滚动的视图放在另一个可滚动的视图中

是一个合理的建议,但是如果您在回收站视图上设置固定高度,它应该可以正常工作。

如果您知道适配器项目布局的高度,您可以计算 RecyclerView 的高度。

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

如何在 recyclerview 中获取 adapterItemSize 任何想法?
它就像一个魅力!应该是一点点更正: int viewHeight = adapterItemSize * adapterData.size(); recyclerView.getLayoutParams().height = viewHeight;
如何找出adapterItemSize?
@JoakimEngstrom adapterItemSize 变量是什么?
避免在滚动视图中使用回收器视图,因为滚动视图为其子视图提供了无限空间。这会导致以 wrap_content 作为高度的回收站视图在垂直方向上无限测量(直到回收站视图的最后一项)。不要在滚动视图中使用回收器视图,而是仅使用具有不同项目类型的回收器视图。使用此实现,滚动视图的子视图将表现为视图类型。在回收站视图中处理这些视图类型。
r
rsicarelli

如果为 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) {

    }
});

确实,这对我来说效果很好。有了这个,我就可以上下移动滚动视图,当我选择 recyclerview 进行滚动时,它优先于滚动视图。谢谢你的提示
它也对我有用,只要确保在滚动视图的直接子级上使用 getParent
此方法也适用于 cyanogenmod 分布。 cyanogenmod 上的固定高度解决方案有效,但前提是固定高度是列表中所有项目的绝对高度,这违背了首先使用 recyclerview 的要点。赞成。
我还需要 recyclerView.setNestedScrollingEnabled(false);
这真的奏效了!我从来没有想过在项目级别设置触摸侦听器。我尝试(没有运气)在 recyclerView 级别设置触摸侦听器。很好的解决方案。干杯!
V
Vadim Kotov

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

Android Support Library 23.2


也不能正常投掷(23.4.0)
@behelit 23.4.0 有一些问题 code.google.com/p/android/issues/detail?id=210085#makechanges ,请改用 23.2.1
甚至在 25.0.1 上都无法正常运行
j
jcady

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

正确的解决方案是在 RecyclerView 高度上使用 wrap_content,然后实现可以正确处理包装的自定义 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,以编程方式膨胀子视图,并将它们添加到布局中。


你不能在 recyclerview 上调用 setNestedScrollingEnabled(false) 吗?
T
Tunaki

对于 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 上方添加布局。
只是 RecyclerView 滚动和 ScrollView 不滚动
V
Vasily Kabunov

手动计算 RecyclerView 的高度不好,最好使用自定义 LayoutManager

上述问题的原因是任何具有滚动(ListViewGridViewRecyclerView)的视图在添加为另一个视图中的子视图具有滚动时都无法计算其高度。所以覆盖它的 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");
    }
}
}

当我们将数据设置为适配器时,我该如何调用这个类?
R
Ranjithkumar

尝试这个。很晚的答案。但肯定会在未来帮助任何人。

将您的 Scrollview 设置为 NestedScrollView

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

在您的回收站视图中

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

在 NestedScrollView 中使用 RecyclerView 会为列表中的每个项目调用 onBindView,即使该项目不可见。这个问题有什么解决办法吗?
您只需将 PaddingBottom 提供给 nestedScrollView 内的 LinearLayout - @thedarkpassenger
L
Linh

如果您将 RecyclerView 放入 NestedScrollView 并启用 recyclerView.setNestedScrollingEnabled(false);,滚动将正常工作
但是,有一个问题

RecyclerView 不回收

例如,您的 RecyclerView(在 NestedScrollViewScrollView 内)有 100 个项目。
Activity 启动时,100 个项目将创建onCreateViewHolderonBindViewHolder 100 个项目将同时调用)。
例如,对于每个项目,您将从 API => 加载一个大图像活动创建->将加载 100 张图片。
它使启动 Activity 缓慢和滞后。
可能的解决方案
- 考虑将 RecyclerView 与多种类型一起使用。

但是,如果在您的情况下,RecyclerView 中只有几个项目,并且 recycledon't recycle 不会对性能产生很大影响,您可以使用 { 1} 在 ScrollView 内简单


展示我如何让 RecyclerView 即使在 ScollView 内部也能回收?谢谢!
@Liar,目前没有办法让 RecyclerView 在放到 ScrollView 后回收。如果你想回收,考虑另一种方法(比如使用多种类型的 RecyclerView)
你可以给回收站视图一个设定的高度
C
Community

更新:这个答案现在已经过时了,因为有像 NestedScrollView 和 RecyclerView 这样的小部件支持嵌套滚动。

您永远不应该将可滚动视图放在另一个可滚动视图中!

我建议你制作你的主要布局回收视图并将你的视图作为回收视图的项目。

看看这个例子,它展示了如何在回收器视图适配器中使用多个视图。 link to example


我有一页有多个回收商,还有其他方法可以说服吗?当你点击更多评论时,像 instagram 或 google play part 评论会加载更多记录
使其成为单个 recyclerView 并将您的视图作为该回收器的项目
那是胡说八道。你可以有嵌套的 RecyclerViews 就好了。
这个答案现在已经过时了。我们有像 NestedScrollView 这样允许嵌套可滚动视图的东西。嵌套的 RecyclerViews 现在也可以工作。
r
royB

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 后仍然有同样的问题。
嗯……你能分享一些代码吗……我的问题为零,但你永远不会知道这类问题
使用此代码为列表中的所有项目调用 onBindView,即使这些项目在列表中不可见。这违背了recyclerview的目的。
i
iDeveloper

您可以使用这种方式:

将此行添加到您的 recyclerView xml 视图中:

        android:nestedScrollingEnabled="false"

试试看,recyclerview 会以灵活的高度平滑滚动

希望这有帮助。


H
Hamza Khan

我使用 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();
    }
}

在 RecyclerView 中使用 CustomLayoutManager:

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>

Z
Zeeshan Shabbir

我遇到了同样的问题。这就是我尝试过的并且有效。我正在分享我的 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 种群(我认为在 RecyclerView 之后计算第一个视图的 Y 位置)
S
Samer

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


仅当您将孩子滚动到视野之外时,这才有效。如果你的孩子是 mapFragments 或 streetviews,这是没有意义的,因为他们每次滚动出 recyclerview 时都被迫重新加载。将它们嵌入到滚动视图中,然后在底部生成回收器视图更有意义。
@Simon 在 ViewHolder 中有方便的 setIsRecyclable()
RecyclerView 替换 ListView 并不是要替换 ScrollView。
这个评论值得更好。这是一个解决方案,甚至比公认的更好。
p
petezurich

这可以解决问题:

recyclerView.setNestedScrollingEnabled(false);

V
Vasily Kabunov

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

S
SANAT

如果 RecyclerView 在 ScrollView 中只显示一行。您只需要将行的高度设置为 android:layout_height="wrap_content"


F
Fred

您还可以覆盖 LinearLayoutManager 以使 recyclerview 顺利滚动

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

S
Shailendra Pundhir

很抱歉迟到了,但似乎还有另一种解决方案非常适合您提到的情况。

如果您在回收站视图中使用回收站视图,它似乎工作得非常好。我亲自尝试并使用过它,它似乎一点也不慢,也不生涩。现在我不确定这是否是一个好习惯,但是嵌套多个回收器视图,甚至嵌套的滚动视图都会变慢。但这似乎工作得很好。请试一试。我相信嵌套会很好。


s
soshial

解决此问题的另一种方法是在 ScrollView 中使用 ConstraintLayout

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

但我仍然会坚持 androidx.core.widget.NestedScrollView approach, proposed by Yang Peiyong


h
harsh patel

您可以尝试将回收站视图 Hight 设置为 wrap_content。就我而言,它工作正常。我正在滚动视图中尝试使用 2 个不同的回收器视图


H
Harshit Jain

最好的解决方案是将 multiple Views 保留在 Single View / View Group 中,然后将那个视图保留在 SrcollView 中。 即。

格式 -

<ScrollView> 
  <Another View>
       <RecyclerView>
       <TextView>
       <And Other Views>
  </Another View>
</ScrollView>

例如。

<ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView           
              android:text="any text"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"/>


        <TextView           
              android:text="any text"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"/>
 </ScrollView>

另一个例如。具有多个视图的 ScrollView

<ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:orientation="vertical"
            android:layout_weight="1">

            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/imageView"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="#FFFFFF"
                />

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:paddingHorizontal="10dp"
                android:orientation="vertical">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:text="@string/CategoryItem"
                    android:textSize="20sp"
                    android:textColor="#000000"
                    />

                <TextView
                    android:textColor="#000000"
                    android:text="₹1000"
                    android:textSize="18sp"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"/>
                <TextView
                    android:textColor="#000000"
                    android:text="so\nugh\nos\nghs\nrgh\n
                    sghs\noug\nhro\nghreo\nhgor\ngheroh\ngr\neoh\n
                    og\nhrf\ndhog\n
                    so\nugh\nos\nghs\nrgh\nsghs\noug\nhro\n
                    ghreo\nhgor\ngheroh\ngr\neoh\nog\nhrf\ndhog"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"/>

             </LinearLayout>

        </LinearLayout>

</ScrollView>

d
danyapd

对于那些试图仅出于设计目的的人 - 离开它。重新设计您的应用程序,只留下 RecyclerView。这将是比执行 ANY 硬编码更好的解决方案。


a
amit singh

**对我有用的解决方案使用高度为 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"

//你的行内容放在这里


Some comments on a similar answer 说禁用嵌套滚动违背了使用 RecyclerView 的目的,即它不会回收视图。不知道如何确认。
设置 nestedScrollingEnabled="false" 会导致 RecyclerView 不回收其视图,至少在我的设置中(NestedScrollView 中的 RecyclerView)。通过将 RecyclerListener 添加到 RecyclerView 并在其 onViewRecycled() 方法中设置断点来确认。