ScrollView内のRecyclerViewが機能しない


278

同じレイアウトでRecyclerViewとScrollViewを含むレイアウトを実装しようとしています。

レイアウトテンプレート:

<RelativeLayout>
    <ScrollView android:id="@+id/myScrollView">
       <unrelated data>...</unrealated data>

           <android.support.v7.widget.RecyclerView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:id="@+id/my_recycler_view"
            />
    </ScrollView>


</RelativeLayout>

問題:最後の要素までスクロールできます ScrollView

私が試したもの:

  1. ScrollView(現在ScrollViewはを含むRecyclerView)内のカードビュー-までカードを見ることができますRecyclerView
  2. 最初の考えは、ビュータイプの1 つがの代わりにこれviewGroupを使用して実装するRecyclerViewことでしScrollViewCardViewが、私はScrollView

このアプローチをチェックアウト:stackoverflow.com/a/21878703/684582
Alécioカルバリョ

15
NestedScrollView多くのスクロールの問題を処理するため、これらのケースの多くで代わりに使用するのが簡単な解決策です
Richard Le Mesurier

リチャードは2月に答えをくれました。のNestedScrollView代わりにを使用してくださいScrollView。それがまさにそのためです。
Mike M.

2
私にとっては変わりません。
ルークアリソン

2
今後の参考のために、マシュマロ/ヌガー(API 23、24)デバイスのみで同様の問題が発生している場合は、私の回避策をstackoverflow.com/a/38995399/132121
Hossain Khan

回答:


654

NestedScrollView代わりに使用ScrollView

詳細については、NestedScrollViewリファレンスドキュメントをご覧ください。

そして追加しrecyclerView.setNestedScrollingEnabled(false);、あなたにRecyclerView


24
対応:android.support.v4.widget.NestedScrollView
Cocorico

7
キープandroid:layout_height="wrap_content"レイアウトのためには、ViewHolderのために膨張
サラス・バブー

4
複雑なレイアウトでNestedScrollViewは、とは異なり、私にとっては時間がかかりScrollViewます。使用せずにソリューションを検索NestedScrollView
Roman Samoylenko 2017

7
また、recyclerView.setNestedScrollingEnabled(false);の代わりに、android:nestedScrollingEnabled = "false"をXMLに追加することもできます。
kostyabakay

2
私にとってはうまくいきましたが、recyclerView内のアイテムがリサイクルされていないことに注意してください。
モスタファArian Nejad

102

私はそれがゲームに遅れていることを知っていますが、グーグルが修正した後でも問題はまだ存在しています android.support.v7.widget.RecyclerView

私は今取得問題があるRecyclerViewlayout_height=wrap_content、すべての項目の高さを服用していないが内部に出すScrollViewことだけマシュマロおよびヌガー+(API 23、24、25)のバージョンで発生します。
(更新:すべてのバージョンScrollViewandroid.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 2017

4
このメソッドはビューをリサイクルしますか?何百ものオブジェクトをリサイクルする必要がある場合。これは解決策ではなくハックです。
androidXP 2017年

1
はい、@ androidXPは正しいです。このハックは長いリストの解決策ではありません。私のユースケースは、10未満のリストビューで修正されたアイテムでした。他の回避策を見つける方法については、ランダムなことを試していましたが、これはその1つでした:-)
Hossain Khan

perfect.adnroidマシュマロとアッパーで私の問題を解決しました。;)
アミールジアラティ2017

2
このソリューションを使用すると、onBindViewがリスト内のすべてのアイテムに対して呼び出されます。これは、recyclerviewのユースケースではありません。
thedarkpassenger

54

その勧告は

スクロール可能なビューを別のスクロール可能なビュー内に配置してはいけません

健全なアドバイスですが、リサイクラービューで固定の高さを設定すると、問題なく機能するはずです。

アダプターアイテムのレイアウトの高さがわかっている場合は、RecyclerViewの高さを計算できます。

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

10
recyclerviewでadapterItemSizeを取得する方法はありますか?
クルティック2015年

1
それは魅力のように機能します!少し修正してください:int viewHeight = adapterItemSize * adapterData.size(); recyclerView.getLayoutParams()。height = viewHeight;
Vaibhav Jani 2015

3
adapterItemSizeを見つける方法は?
droidster.me 2015

2
@JoakimEngstrom adapterItemSize変数は何ですか?
IgorGanapolsky

1
スクロールビューは子に無限のスペースを与えるので、スクロールビュー内のリサイクラービューは避けてください。これにより、wrap_contentを高さとして持つリサイクラービューが垂直方向に無限に測定されます(リサイクラービューの最後のアイテムまで)。スクロールビュー内でリサイクラービューを使用する代わりに、異なるアイテムタイプのリサイクラービューのみを使用してください。この実装では、スクロールビューの子はビュータイプとして動作します。それらのビュータイプをリサイクラービュー内で処理します。
abhishesh 2016年

28

RecyclerViewの固定高さの設定が誰か(私のような)で機能しなかった場合、固定高さソリューションに追加したのは次のとおりです。

mRecyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
        int action = e.getAction();
        switch (action) {
            case MotionEvent.ACTION_MOVE:
                rv.getParent().requestDisallowInterceptTouchEvent(true);
                break;
        }
        return false;
    }

    @Override
    public void onTouchEvent(RecyclerView rv, MotionEvent e) {

    }

    @Override
    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

    }
});

1
確かに、これは私にはうまくいきました。これで、スクロールビューを上下に移動できました。スクロールするリサイクラービューを選択すると、スクロールビューよりも優先されます。ヒントをありがとう
PGMacDesign

1
それも私にとってはうまくいきました。スクロールビューの直接の子でgetParentを使用してください
BigBen3216

この方法は、cyanogenmod分布でも機能します。cyanogenmodの固定高さソリューションは機能しますが、固定高さがリストのすべてのアイテムの絶対高さであり、そもそもrecyclerviewを使用するポイントに反する場合のみです。賛成。
HappyKatz

また、recyclerView.setNestedScrollingEnabled(false);も必要でした。
アナライザ2016


15

RecyclerViewsは、自分自身でスクロールしない限り、ScrollViewに配置しても問題ありません。この場合、高さを固定することは理にかなっています。

適切な解決策はwrap_content、RecyclerViewの高さで使用し、ラッピングを適切に処理できるカスタムのLinearLayoutManagerを実装することです。

このLinearLayoutManagerをプロジェクトにコピーします。https//github.com/serso/android-linear-layout-manager/blob/master/lib/src/main/java/org/solovyev/android/views/llm/LinearLayoutManager.java

次に、RecyclerViewをラップします。

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

そして、次のように設定します。

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

編集:RecyclerViewがScrollViewのタッチイベントを盗む可能性があるため、これによりスクロールが複雑になる可能性があります。私の解決策は、単にRecyclerViewを捨てて、LinearLayoutを使用し、プログラムでサブビューを膨らませ、それらをレイアウトに追加することでした。


4
recyclerviewで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 2016年

RecyclerViewがスクロールし、ScrollViewがスクロールしない
Samad

13

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

データをアダプターに設定するときに、このクラスをどのように呼び出すことができますか?
ミラノガヘラ2016

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

8

更新:ネストされたスクロールをサポートするNestedScrollViewやRecyclerViewなどのウィジェットがあるため、この回答は古くなっています。

スクロール可能なビューを別のスクロール可能なビュー内に配置してはいけません。

メインレイアウトのリサイクラービューを作成し、ビューをリサイクラービューのアイテムとして配置することをお勧めします。

この例を見ると、リサイクラービューアダプター内で複数のビューを使用する方法がわかります。 例へのリンク


複数のRecyclerを含むページがありますが、これを説得する他の方法はありますか?一部のようなもののInstagramプレイグーグルあなたはをクリックしたときの負荷がより記録することを一部のコメントをもっとコメント
Ashkan

単一のrecyclerViewにして、ビューをそのリサイクラーのアイテムとして配置する
EC84B4

6
それはナンセンスです。入れ子になったRecyclerViewは問題なく使用できます。
IgorGanapolsky 2015

この回答は古くなっています。ネストされたスクロール可能なビューを可能にするNestedScrollViewなどがあります。ネストされたRecyclerViewsも機能するようになりました。
コーリーホーン

7

あなたが入れた場合RecyclerViewの内部NestedScrollViewと有効recyclerView.setNestedScrollingEnabled(false);、スクロールしますうまく機能
しかし、問題あります

RecyclerView リサイクルしないでください

たとえば、RecyclerView(内部NestedScrollViewまたはScrollView)には100個のアイテムがあります。
ときActivity打ち上げは、100項目が作成されますonCreateViewHolderonBindViewHolder100項目の同じ時刻に呼ばれます)。
例:各アイテムについて、APIから大きな画像を読み込みます=>作成されたアクティビティ-> 100画像が読み込まれます。
これは、アクティビティの開始を遅らせ、遅らせます。
考えられる解決策
- RecyclerView複数のタイプでの使用について考えます

ただし、あなたの場合、いくつかのアイテムがRecyclerViewあり、リサイクルまたはリサイクルしないことがパフォーマンスに大きな影響を与えない場合は、RecyclerView内部ScrollViewで簡単に使用できます


ScollView内でもRecyclerViewをリサイクルできるようにする方法を示しますか?ありがとう!
嘘つき

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 2016

うーん...あなたはいくつかのコードを共有できますか...私はゼロの問題を抱えていますが、あなたはこの種の問題で決して知ることができません
royB

メールまたはスタックオーバーフローを介してコードを共有するには
MohanRaj S

2
このコードを使用すると、リストに表示されていないアイテムでも、リスト内のすべてのアイテムに対してonBindViewが呼び出されます。これはrecyclerviewの目的を無効にします。
thedarkpassenger

2

CustomLayoutManagerを使用してRecyclerViewスクロールを無効にしました。また、WrapContentとしてRecycler Viewを使用しないでください。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 Save

それも機能します。あなただけを使用する必要があります。NestedScrollViewスクロールビューの代わり
Zeeshan Shabbir 2017

はい、NestedScrollViewのみです。しかし、NestedScrollView + RecycleViewを使用したソリューションは、RecyclerViewの非常に遅い母集団を呼び出します(RecyclerViewの後の最初のビューのY位置の計算を考える)
Tapa Save


0

実際、の主な目的は、およびRecyclerViewを補正することです。実際に行っていることを行うのではなく、にa を含めると、多くのタイプの子を処理できるだけを持つことをお勧めします。ListViewScrollViewRecyclerViewScrollViewRecyclerView


1
これは、スクロールしてビューから外すとすぐに子供がゴミ収集できる場合にのみ機能します。mapFragmentsまたはストリートビューである子がある場合、recyclerviewからスクロールするたびに強制的に再ロードされるため、意味がありません。それらをscrollviewに埋め込み、下部にrecyclerviewを生成することは、より意味があります。
Simon

1
@Simon ViewHolderには便利なsetIsRecyclable()があります
ernazm

RecyclerViewはListViewを置き換えるもので、ScrollViewを置き換えるものではありません。
2016年

このコメントはもっと価値があります。それは解決策であり、受け入れられているものよりも優れています。
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内の1行のみを表示する場合。行の高さをに設定するだけですandroid:layout_height="wrap_content"


0

LinearLayoutManagerをオーバーライドして、recyclerviewをスムーズに回転させることもできます

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

0

パーティーに遅れて申し訳ありませんが、あなたが言及した場合に完全に機能する別の解決策があるようです。

リサイクラービュー内でリサイクラービューを使用すると、完全に正常に動作するように見えます。個人的に試して使ってみましたが、動作の鈍さやぎくしゃく感はまったくないようです。これが良い習慣であるかどうかはわかりませんが、複数のリサイクラービューをネストすると、ネストしたスクロールビューでも速度が低下します。しかし、これはうまく機能するようです。ぜひお試しください。これでネストは完全にうまくいくと思います。


0

この問題に対処する別のアプローチは、ConstraintLayout内部で使用することScrollViewです:

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

しかし、私は依然としてandroidx.core.widget.NestedScrollView ヤン・ペイヨンが提案したアプローチに固執します。


-2

**私のために働いた解決策
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.