私はこの同じ問題に遭遇し、それを解決するためにほぼ一日を費やしました.
前提条件:
まず、私の xml レイアウトは次のようになります。
<CoordinatorLayout>
<com.google.android.material.appbar.AppBarLayout
...
</com.google.android.material.appbar.AppBarLayout>
<NestedScrollView>
<RecyclerView/>
</NestedScrollView>
</CoordinatorLayout>
また、スクロール動作を正常にするために、次のようにnestedScrolling
してRecyclerView
無効にします。RecyclerView.setIsNestedScrollingEnabled(false);
理由:
しかし、アイテムをドラッグしても、期待どおりに自動スクロールをItemTouchHelper
行うことはできません。IT CANNOT SCROLLRecyclerview
の理由は次の方法にあります。scrollIfNecessary()
ItemTouchHelper
boolean scrollIfNecessary() {
RecyclerView.LayoutManager lm = mRecyclerView.getLayoutManager();
if (mTmpRect == null) {
mTmpRect = new Rect();
}
int scrollY = 0;
lm.calculateItemDecorationsForChild(mSelected.itemView, mTmpRect);
if (lm.canScrollVertically()) {
int curY = (int) (mSelectedStartY + mDy);
final int topDiff = curY - mTmpRect.top - mRecyclerView.getPaddingTop();
if (mDy < 0 && topDiff < 0) {
scrollY = topDiff;
} else if (mDy > 0) {
final int bottomDiff = curY + mSelected.itemView.getHeight() + mTmpRect.bottom
- (mRecyclerView.getHeight() - mRecyclerView.getPaddingBottom());
if (bottomDiff > 0) {
scrollY = bottomDiff;
}
}
}
if (scrollY != 0) {
scrollY = mCallback.interpolateOutOfBoundsScroll(mRecyclerView,
mSelected.itemView.getHeight(), scrollY,
mRecyclerView.getHeight(), scrollDuration);
}
if (scrollY != 0) {
mRecyclerView.scrollBy(scrollX, scrollY);
return true;
}
return false;
}
- 理由 1:がfalse に設定され
nestedScrolling
てRecyclerView
いる場合、実際に有効なスクロール オブジェクトはNestedScrollView
の親であるRecyclerView
です。したがって、RecyclerView.scrollBy(x, y)
ここではまったく機能しません。
- 理由 2:
mRecyclerView.getHeight()
よりもはるかに大きいNestedScrollView.getHeight()
。そのため、アイテムRecyclerView
を下にドラッグすると、結果scrollIfNecessary()
も false になります。
- 理由 3:
mSelectedStartY
私たちの場合、期待値のようには見えません。scrollY
この場合、を計算する必要があるためNestedScrollView
です。
したがって、このメソッドをオーバーライドして、期待を満たす必要があります。ここに解決策があります:
解決:
ステップ1:
これをオーバーライドするにscrollIfNecessary()
は (このメソッドは ではありませんpublic
)、 と同じ名前のパッケージの下に新しいクラスを作成する必要がありますItemTouchHelper
。このような:
ステップ2:
のオーバーライドに加えて、ドラッグ開始時にとの値を取得するためにscrollIfNecessary()
もオーバーライドする必要があります。select()
mSelectedStartY
scrollY
NestedScrollView
public override fun select(selected: RecyclerView.ViewHolder?, actionState: Int) {
super.select(selected, actionState)
if (selected != null) {
mSelectedStartY = selected.itemView.top
mSelectedStartScrollY = (mRecyclerView.parent as NestedScrollView).scrollY.toFloat()
}
}
注意: mSelectedStartY
とは両方とも、上下mSelectedStartScrollY
にスクロールするために非常に重要です。NestedScrollView
ステップ 3:
これで をオーバーライドできるようscrollIfNecessary()
になりました。以下のコメントに注意する必要があります。
public override fun scrollIfNecessary(): Boolean {
...
val lm = mRecyclerView.layoutManager
if (mTmpRect == null) {
mTmpRect = Rect()
}
var scrollY = 0
val currentScrollY = (mRecyclerView.parent as NestedScrollView).scrollY
// We need to use the height of NestedScrollView, not RecyclerView's!
val actualShowingHeight = (mRecyclerView.parent as NestedScrollView).height
lm!!.calculateItemDecorationsForChild(mSelected.itemView, mTmpRect!!)
if (lm.canScrollVertically()) {
// The true current Y of the item in NestedScrollView, not in RecyclerView!
val curY = (mSelectedStartY + mDy - currentScrollY).toInt()
// The true mDy should plus the initial scrollY and minus current scrollY of NestedScrollView
val checkDy = (mDy + mSelectedStartScrollY - currentScrollY).toInt()
val topDiff = curY - mTmpRect!!.top - mRecyclerView.paddingTop
if (checkDy < 0 && topDiff < 0) {// User is draging the item out of the top edge.
scrollY = topDiff
} else if (checkDy > 0) { // User is draging the item out of the bottom edge.
val bottomDiff = (curY + mSelected.itemView.height + mTmpRect!!.bottom
- (actualShowingHeight - mRecyclerView.paddingBottom))
if (bottomDiff > 0) {
scrollY = bottomDiff
}
}
}
if (scrollY != 0) {
scrollY = mCallback.interpolateOutOfBoundsScroll(
mRecyclerView,
mSelected.itemView.height, scrollY, actualShowingHeight, scrollDuration
)
}
if (scrollY != 0) {
...
// The scrolling behavior should be assigned to NestedScrollView!
(mRecyclerView.parent as NestedScrollView).scrollBy(0, scrollY)
return true
}
...
return false
}
結果:
下のGifで私の作品をお見せできます: