[英]ItemTouchHelper with RecyclerView in NestedScrollView: Drag scrolling not work
I have implemented ItemTouchHelper like descriped in this articel: https:\/\/medium.com\/@ipaulpro\/drag-and-swipe-with-recyclerview-b9456d2b1aaf#.k7xm7amxi<\/a>我已经像这篇文章中描述的那样实现了 ItemTouchHelper:
https<\/a> :\/\/medium.com\/@ipaulpro\/drag-and-swipe-with-recyclerview-b9456d2b1aaf#.k7xm7amxi
您必须为recyclerView
禁用nestedScrolling
:
recyclerView.setIsNestedScrollingEnabled(false);
I have run into this same problem and I spent nearly a whole day to solve it.我遇到了同样的问题,我花了将近一整天的时间来解决它。
First of all, my xml layout looks like this:首先,我的 xml 布局如下所示:
<CoordinatorLayout>
<com.google.android.material.appbar.AppBarLayout
...
</com.google.android.material.appbar.AppBarLayout>
<NestedScrollView>
<RecyclerView/>
</NestedScrollView>
</CoordinatorLayout>
And to make the scrolling behavior normal I also let the nestedScrolling
for the RecyclerView
disabled by: RecyclerView.setIsNestedScrollingEnabled(false);
为了使滚动行为正常,我还让
RecyclerView
的nestedScrolling
被禁用: RecyclerView.setIsNestedScrollingEnabled(false);
But with ItemTouchHelper
I still cannot make the Recyclerview
auto scroll when I drag the item in it.但是使用
ItemTouchHelper
,当我将项目拖入其中时,我仍然无法使Recyclerview
自动滚动。 The reason why IT CANNOT SCROLL is in the method scrollIfNecessary()
of ItemTouchHelper
: IT CANNOT SCROLL的原因在于 ItemTouchHelper 的
ItemTouchHelper
scrollIfNecessary()
方法:
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;
}
nestedScrolling
for the RecyclerView
is set to false, actually the effective scrolling object is the NestedScrollView
, which is the parent of RecyclerView
.RecyclerView
的nestedScrolling
设置为 false 时,实际上有效的滚动对象是NestedScrollView
,它是RecyclerView
的父对象。 So RecyclerView.scrollBy(x, y)
here does not work at all!RecyclerView.scrollBy(x, y)
根本不起作用!mRecyclerView.getHeight()
is much bigger than NestedScrollView.getHeight()
.mRecyclerView.getHeight()
比NestedScrollView.getHeight()
大得多。 So when I drag the item in RecyclerView
to bottom, the result of scrollIfNecessary()
is also false.RecyclerView
中的项目拖到底部时, scrollIfNecessary()
的结果也是错误的。mSelectedStartY
does not seem like the expected value when in our case.mSelectedStartY
看起来不像预期的值。 Because we need to calculate the scrollY
of NestedScrollView
in our case.scrollY
的NestedScrollView
。 Therefore, we need to override this method to fullfill our expectation.因此,我们需要重写这个方法来满足我们的期望。 Here comes the solution:
解决方案来了:
In order to override this scrollIfNecessary()
(This method is not public
), you need to new a class under a package named the same as ItemTouchHelper
.为了覆盖此
scrollIfNecessary()
(此方法不是public
),您需要在与ItemTouchHelper
同名的包下新建一个类。 Like this:像这样:
Besides overriding scrollIfNecessary()
, we also need to override select()
in order to get the value of mSelectedStartY
and the scrollY
of NestedScrollView
when starting draging.除了覆盖
scrollIfNecessary()
之外,我们还需要覆盖select()
以便在开始拖动时获取scrollY
mSelectedStartY
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()
}
}
Notice: mSelectedStartY
and mSelectedStartScrollY
are both very important for scrolling the NestedScrollView
up or down.注意:
mSelectedStartY
和mSelectedStartScrollY
对于NestedScrollView
向上或向下滚动都非常重要。
Now we can override scrollIfNecessary()
, and you need to pay attention to the comments below:现在我们可以覆盖
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
}
I can just show you my work through the Gif below:我可以通过下面的 Gif 向您展示我的工作:
This is the solution that works for me.这是对我有用的解决方案。
Create 2 custom classes创建 2 个自定义类
1> LockableScrollView 1> LockableScrollView
public class LockableScrollView extends NestedScrollView {公共类 LockableScrollView 扩展 NestedScrollView {
// true if we can scroll (not locked)
// false if we cannot scroll (locked)
private boolean mScrollable = true;
public LockableScrollView(@NonNull Context context) {
super(context);
}
public LockableScrollView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public LockableScrollView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setScrollingEnabled(boolean enabled) {
mScrollable = enabled;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// Don't do anything with intercepted touch events if
// we are not scrollable
if (ev.getAction() == MotionEvent.ACTION_MOVE) {// if we can scroll pass the event to the superclass
return mScrollable && super.onInterceptTouchEvent(ev);
}
return super.onInterceptTouchEvent(ev);
}
android:descendantFocusability="blocksDescendants"
android:descendantFocusability="blocksDescendants"
add in NestedScrollView and add添加NestedScrollView并添加
android:focusableInTouchMode="true"
android:focusableInTouchMode="true"
in child layout it look like below在子布局中它看起来像下面
<androidx.core.widget.NestedScrollView
android:descendantFocusability="blocksDescendants">
<androidx.constraintlayout.widget.ConstraintLayout
android:focusableInTouchMode="true">
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
check this github repo https://github.com/khambhaytajaydip/Drag-Drop-recyclerview
检查这个 github 仓库https://github.com/khambhaytajaydip/Drag-Drop-recyclerview
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.