我下面有一个Activity
:
<RecyclerView> // with horizontal LinearLayoutManager and PagerSnapHelper
<RecyclerView> // with vertical LinearLayoutManager
<RecyclcerView /> // with horizontal LinearLayoutManager
<HorizontalScrollView />
// ... and there are other normal list items
</RecyclerView>
<RecyclerView> // another RV with vertical LinearLayoutManager
</RecyclerView>
</RecyclerView>
问题是当我水平滚动时,内部水平RecyclerView
和HorizontalScrollView
不能按预期滚动。
为什么必须这样做:
我有几个ListFragment
,每个都从不同的数据源获取数据,但是具有相同类型的列表项。我不需要ViewPager
,它要么在创建ViewPager
之后立即保存所有页面(这可能导致OutOfMemoryError),要么在用户滚动到其他页面并向后滚动后从网络重新加载数据。此外,借助嵌套的RV,页面可以共享一个相同的RecyclerViewPool
,这有助于减少所有页面中项目视图的总数。
我在外侧水平RV中所做的事情:
private static final int INVALID_POINTER = -1;
private int mActivePointerId = INVALID_POINTER;
private float lastX;
private GestureDetector mVerticalScrollGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return Math.abs(distanceX) < Math.abs(distanceY);
}
});
private GestureDetector mHorizontalScrollGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return Math.abs(distanceX) > Math.abs(distanceY);
}
});
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
if (mVerticalScrollGestureDetector.onTouchEvent(e) || !mAllowPageScroll) {
return false;
}
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
mActivePointerId = e.getPointerId(0);
break;
case MotionEvent.ACTION_MOVE:
int pointerIndex = e.findPointerIndex(mActivePointerId);
float x = e.getX(pointerIndex);
float dx = x - lastX;
lastX = x;
if (childCanScroll(this, false, (int) dx, (int) x)) {
Log.d("ScrollX", "not intercept");
return false;
}
Log.d("ScrollX", "maybe intercept");
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mActivePointerId = INVALID_POINTER;
break;
}
return super.onInterceptTouchEvent(e);
}
private boolean childCanScroll(View v, boolean considerSelf, int dx, int x) {
if (v instanceof ViewGroup) {
ViewGroup group = (ViewGroup) v;
int scrollX = v.getScrollX();
for (int i = group.getChildCount() - 1; i >= 0; i--) {
View child = group.getChildAt(i);
if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight()
&& childCanScroll(child, true, dx, x + scrollX - child.getLeft())) {
return true;
}
}
}
return considerSelf && v.canScrollHorizontally(-dx);
}
现在它可以与内部水平RV配合使用,有时不滚动,并且根本不适用于HorizontalScrollView
。
如何实现使内部水平滚动视图首先滚动的目标?
答案 0 :(得分:0)
我犯了两个错误。
在ACTION_DOWN
的情况下,我还需要记录lastX
变量,在ACTION_MOVE
的情况下,因此dx
的第一步是合适的值。这就解释了为什么childCanScroll
方法在ScrollView
之类的东西上不起作用(因为RecyclerView
的{{1}}总是返回0)。
在方法getScrollX()
中,内部childCanScroll
语句不仅应考虑x坐标,还应考虑y坐标。如果不考虑y,则当我在内部水平滚动视图外部滚动时,该方法还会返回if
,这会使事情无法正常工作。
我纠正了这两个错误后,一切都正确了。