简体   繁体   English

ScrollView 内的 RecyclerView 不工作

[英]RecyclerView inside ScrollView is not working

I'm trying to implement a layout which contains RecyclerView and ScrollView at the same layout.我正在尝试在同一布局中实现一个包含RecyclerViewScrollView的布局。

Layout template:布局模板:

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

Problems: I can scroll until the last element of ScrollView .问题:我可以滚动到ScrollView的最后一个元素。

Things I tried:我尝试过的事情:

  1. Card view inside the ScrollView (now ScrollView contains RecyclerView ) - can see the card up until the RecyclerView . ScrollView内的卡片视图(现在ScrollView包含RecyclerView )- 可以看到卡片直到RecyclerView
  2. Initial thought was to implement this ViewGroup using RecyclerView instead of ScrollView where one of it's views type is the CardView , but I got the exact same results as with the ScrollView .最初的想法是使用RecyclerView而不是ScrollView来实现这个ViewGroup ,其中一个视图类型是CardView ,但我得到的结果与ScrollView完全相同。

use NestedScrollView instead of ScrollView使用NestedScrollView而不是ScrollView

Please go through NestedScrollView reference document for more information.请查看NestedScrollView 参考文档以获取更多信息。

and add recyclerView.setNestedScrollingEnabled(false);并添加recyclerView.setNestedScrollingEnabled(false); to your RecyclerView到您的RecyclerView

I know I am late it the game, but the issue still exists even after google has made fix on the android.support.v7.widget.RecyclerView我知道我迟到了,但即使谷歌对android.support.v7.widget.RecyclerView进行了修复,问题仍然存在

The issue I get now is RecyclerView with layout_height=wrap_content not taking height of all the items issue inside ScrollView that only happens on Marshmallow and Nougat+ (API 23, 24, 25) versions.我现在遇到的问题是RecyclerViewlayout_height=wrap_content没有考虑ScrollView中所有项目的高度,这发生在 Marshmallow 和 Nougat+(API 23、24、25)版本上。
(UPDATE: Replacing ScrollView with android.support.v4.widget.NestedScrollView works on all versions. I somehow missed testing accepted solution . Added this in my github project as demo.) (更新:用android.support.v4.widget.NestedScrollView替换ScrollView适用于所有版本。我不知何故错过了测试接受的解决方案。在我的 github 项目中添加了这个作为演示。)

After trying different things, I have found workaround that fixes this issue.在尝试了不同的事情后,我找到了解决此问题的解决方法。

Here is my layout structure in a nutshell:简而言之,这是我的布局结构:

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

The workaround is the wrap the RecyclerView with RelativeLayout .解决方法是用RelativeLayout包装RecyclerView Don't ask me how I found this workaround!!!不要问我是如何找到这个解决方法的!!! ¯\\_(ツ)_/¯

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

Complete example is available on GitHub project - https://github.com/amardeshbd/android-recycler-view-wrap-content GitHub项目上提供了完整的示例 - https://github.com/amardeshbd/android-recycler-view-wrap-content

Here is a demo screencast showing the fix in action:这是一个演示截屏视频,显示了正在执行的修复:

截屏

Although the recommendation that虽然建议

you should never put a scrollable view inside another scrollable view你永远不应该把一个可滚动视图放在另一个可滚动视图中

Is a sound advice, however if you set a fixed height on the recycler view it should work fine.是一个合理的建议,但是如果您在回收站视图上设置固定高度,它应该可以正常工作。

If you know the height of the adapter item layout you could just calculate the height of the RecyclerView.如果您知道适配器项目布局的高度,您只需计算 RecyclerView 的高度即可。

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

In case setting fixed height for the RecyclerView didn't work for someone (like me), here is what I've added to the fixed height solution:如果为 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) {

    }
});

The new Android Support Library 23.2 solves that problem, you can now set wrap_content as the height of your RecyclerView and works correctly.新的Android 支持库 23.2解决了这个问题,您现在可以将wrap_content设置为RecyclerView的高度并正常工作。

Android Support Library 23.2 Android 支持库 23.2

RecyclerViews are fine to put in ScrollViews so long as they aren't scrolling themselves.只要 RecyclerViews 本身不滚动,就可以放入 ScrollViews 中。 In this case, it makes sense to make it a fixed height.在这种情况下,将其设置为固定高度是有意义的。

The proper solution is to use wrap_content on the RecyclerView height and then implement a custom LinearLayoutManager that can properly handle the wrapping.正确的解决方案是在 RecyclerView 高度上使用wrap_content ,然后实现一个可以正确处理包装的自定义 LinearLayoutManager 。

Copy this LinearLayoutManager into your project: https://github.com/serso/android-linear-layout-manager/blob/master/lib/src/main/java/org/solovyev/android/views/llm/LinearLayoutManager.java将此 LinearLayoutManager 复制到您的项目中: https : //github.com/serso/android-linear-layout-manager/blob/master/lib/src/main/java/org/solovyev/android/views/llm/LinearLayoutManager.java

Then wrap the RecyclerView:然后包装 RecyclerView:

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

And set it up like so:并像这样设置:

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

Edit: This can cause complications with scrolling because the RecyclerView can steal the ScrollView's touch events.编辑:这可能会导致滚动复杂化,因为 RecyclerView 可以窃取 ScrollView 的触摸事件。 My solution was just to ditch the RecyclerView in all and go with a LinearLayout, programmatically inflate subviews, and add them to the layout.我的解决方案是完全抛弃 RecyclerView 并使用 LinearLayout,以编程方式膨胀子视图,并将它们添加到布局中。

For ScrollView , you could use fillViewport=true and make layout_height="match_parent" as below and put recycler view inside:对于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>

No further height adjustment needed through code.无需通过代码进一步调整高度。

Calculating RecyclerView 's height manually is not good, better is to use a custom LayoutManager .手动计算RecyclerView的高度不好,最好使用自定义LayoutManager

The reason for above issue is any view which has it's scroll( ListView , GridView , RecyclerView ) failed to calculate it's height when add as a child in another view has scroll.上述问题的原因是任何具有滚动( ListViewGridViewRecyclerView )的视图在另一个视图中作为子项添加滚动时无法计算其高度。 So overriding its onMeasure method will solve the issue.所以覆盖它的onMeasure方法将解决这个问题。

Please replace the default layout manager with the below:请将默认布局管理器替换为以下内容:

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

Try this.尝试这个。 Very late answer.很晚的答案。 But surely help anyone in future.但肯定会在未来帮助任何人。

Set your Scrollview to NestedScrollView将您的 Scrollview 设置为 NestedScrollView

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

In your Recyclerview在您的回收站视图中

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

If you put RecyclerView inside NestedScrollView and enable recyclerView.setNestedScrollingEnabled(false);如果你把RecyclerView放在NestedScrollView并启用recyclerView.setNestedScrollingEnabled(false); , scrolling will working well . ,滚动将工作良好
However, there is a problem然而,有一个问题

RecyclerView don't recycle RecyclerView不回收

For example, your RecyclerView (inside NestedScrollView or ScrollView ) have 100 item.例如,您的RecyclerView (在NestedScrollViewScrollView )有 100 个项目。
When Activity launch, 100 item will create ( onCreateViewHolder and onBindViewHolder of 100 item will called at same time).Activity启动时,将创建100 个项目(同时onBindViewHolder 100 个项目的onCreateViewHolderonBindViewHolder )。
Example, for each item, you will load a large image from API => activity created -> 100 image will load.例如,对于每个项目,您将从 API => 活动创建 -> 100 张图像将加载一个大图像。
It make starting Activity slowness and lagging.它使启动 Activity 缓慢和滞后。
Possible solution :可能的解决方案
- Thinking about using RecyclerView with multiple type. - 考虑使用多种类型的RecyclerView

However, if in your case, there are just a few item in RecyclerView and recycle or don't recycle don't affect performance a lot, you can use RecyclerView inside ScrollView for simple但是,如果在您的情况下, RecyclerView中只有几个项目并且回收不回收不会对性能产生很大影响,您可以在ScrollView使用RecyclerView进行简单

UPDATE: this answer is out dated now as there are widgets like NestedScrollView and RecyclerView that support nested scrolling.更新:这个答案现在已经过时了,因为有像 NestedScrollView 和 RecyclerView 这样支持嵌套滚动的小部件。

you should never put a scrollable view inside another scrollable view !你永远不应该把一个可滚动视图放在另一个可滚动视图中!

i suggest you make your main layout recycler view and put your views as items of recycler view.我建议您制作主布局回收器视图并将您的视图作为回收器视图的项目。

take a look at this example it show how to use multiple views inside recycler view adapter.看看这个例子,它展示了如何在回收器视图适配器中使用多个视图。 link to example链接到示例

It seems that NestedScrollView does solve the problem.看来NestedScrollView确实解决了这个问题。 I've tested using this layout:我已经使用这种布局进行了测试:

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

And it works without issues它可以毫无问题地工作

You can use this way either :您可以使用这种方式:

Add this line to your recyclerView xml view :将此行添加到您的 recyclerView xml 视图中:

        android:nestedScrollingEnabled="false"

try it ,recyclerview will be smoothly scrolled with flexible height试试看,recyclerview 会以灵活的高度平滑滚动

hope this helped .希望这有帮助。

I was having the same problem.我遇到了同样的问题。 That's what i tried and it works.这就是我尝试过的并且有效。 I am sharing my xml and java code.我正在分享我的 xml 和 java 代码。 Hope this will help someone.希望这会帮助某人。

Here is the xml这是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 >

Here is the related java code.这是相关的java代码。 It works like a charm.它就像一个魅力。

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

I used CustomLayoutManager to disable RecyclerView Scrolling.我使用 CustomLayoutManager 禁用 RecyclerView 滚动。 Also don't use Recycler View as WrapContent, use it as 0dp, Weight=1也不要将 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();
    }
}

Use CustomLayoutManager in RecyclerView:在 RecyclerView 中使用 CustomLayoutManager:

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

UI XML:用户界面 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>

Actually the main purpose of the RecyclerView is to compensate for ListView and ScrollView .实际上RecyclerView的主要目的是补偿ListViewScrollView Instead of doing what you're actually doing: Having a RecyclerView in a ScrollView , I would suggest having only a RecyclerView that can handle many types of children.与其做你真正在做的事情:在ScrollView有一个RecyclerView ,我建议只拥有一个可以处理多种类型的孩子的RecyclerView

这是诀窍:

recyclerView.setNestedScrollingEnabled(false);

First you should use NestedScrollView instead of ScrollView and put the RecyclerView inside NestedScrollView .首先,您应该使用NestedScrollView而不是ScrollView并将RecyclerView放在NestedScrollView

Use Custom layout class to measure the height and width of screen:使用自定义布局类来测量屏幕的高度和宽度:

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

And implement below code in the activity/fragment of RecyclerView :并在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!");
            }
        }
    });

If RecyclerView showing only one row inside ScrollView.如果 RecyclerView 在 ScrollView 中只显示一行。 You just need to set height of your row to android:layout_height="wrap_content" .您只需要将行的高度设置为android:layout_height="wrap_content"

you can also override LinearLayoutManager to make recyclerview roll smoothly您还可以覆盖 LinearLayoutManager 使 recyclerview 顺利滚动

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

Sorry being late to the party, but it seems like there is another solution which works perfectly for the case you mentioned.很抱歉迟到了,但似乎有另一种解决方案可以完美地适用于您提到的情况。

If you use a recycler view inside a recycler view, it seems to work perfectly fine.如果您在回收站视图中使用回收站视图,它似乎工作得很好。 I have personally tried and used it, and it seems to give no slowness and no jerkyness at all.我亲自尝试并使用过它,它似乎完全没有缓慢和生涩。 Now I am not sure if this is a good practice or not, but nesting multiple recycler views , even nested scroll view slows down.现在我不确定这是否是一个好的做法,但是嵌套多个回收器视图,甚至嵌套的滚动视图也会变慢。 But this seems to work nicely.但这似乎工作得很好。 Please give it a try.请试一试。 I am sure nesting is going to be perfectly fine with this.我相信嵌套会非常好。

Another approach to address the issue is to use ConstraintLayout inside ScrollView :解决此问题的另一种方法是在ScrollView使用ConstraintLayout

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

But I would still stick to the androidx.core.widget.NestedScrollView approach, proposed by Yang Peiyong .但我仍然会坚持使用androidx.core.widget.NestedScrollView提出androidx.core.widget.NestedScrollView方法

You can try with setting recycler view Hight as wrap_content.您可以尝试将回收器视图高度设置为 wrap_content。 in my case its working fine.就我而言,它工作正常。 I am trying with 2 different recycler view in scroll view我正在尝试在滚动视图中使用 2 个不同的回收器视图

The best solution is to keep multiple Views in a Single View / View Group and then keep that one view in the SrcollView.最好的解决方案是在Single View / View Group保留multiple Views ,然后在 SrcollView 中保留该视图。 ie. IE。

Format -格式-

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

Eg.例如。

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

Another Eg.另一个例如。 of ScrollView with multiple Views具有多个视图的 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>

For those people who trying to do it just for design purposes - leave it.对于那些只是出于设计目的而尝试这样做的人 - 离开它。 Redesign your app and leave only RecyclerView .重新设计你的应用程序,只留下RecyclerView It will be better solution than doing ANY hardcode.这将是比做任何硬编码更好的解决方案。

The Best Way Is To set最好的方法是设置

recyclerView.setNestedScrollingEnabled(false);

in java class在java类中

**Solution which worked for me **对我有用的解决方案
Use NestedScrollView with height as wrap_content使用高度为 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"

//Your row content goes here //你的行内容放在这里

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM