我有一个viewpager,在其中一个片段中,我有两个独立的片段,分别包含一个垂直和水平的Recyclerview。
当我将水平回收视图滚动到最后一项并尝试进一步滑动时,viewpager会滚动到下一页。我不希望这种情况发生。当我尝试过度滚动水平回收视图时,我想禁用viewpager的分页。
但是,当我在其他地方滑动时,我不想禁用viewpager的分页。例如,如果我要在垂直的recyclelerview或父片段中的任何空白区域上滑动,它仍然会导致viewpager更改页面。
我读了in this SO question,如何禁用viewpager的分页。同样 this SO question也是类似的,因为有一个子viewpager,但是我没有成功尝试使用水平Recyclerview复制它。
这是一些结构:
自定义viewpager允许我禁用分页(从上面的第一个SO链接获取):
public class CustomViewPager extends ViewPager {
private boolean enabled;
public CustomViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
this.enabled = true;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (this.enabled) {
return super.onTouchEvent(event);
}
return false;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (this.enabled) {
return super.onInterceptTouchEvent(event);
}
return false;
}
public void setPagingEnabled(boolean enabled) {
this.enabled = enabled;
} }
对于水平回收者视图,我设置了ontouchlistener(类似于上面的第二个SO链接):
horizontalRecyclerView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction()){
case MotionEvent.ACTION_MOVE:
customViewPager.setPagingEnabled(false);
break;
case MotionEvent.ACTION_UP:
customViewPager.setPagingEnabled(true);
break;
}
return false;
}
});
额外的观察:我注意到有时如果我在滑动之前长按水平回收视图,它将不会转到viewpager的下一页。但是,如果我快速滑动,viewpager将转到下一页。
有谁知道这样做的正确方法?
感谢任何帮助。
答案 0 :(得分:18)
这是解决方案。
requestDisallowInterceptTouchEvent()
此方法不允许父移动。它会停止viewpager触摸事件。
horizontalRecyclerView.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 :(得分:1)
为了处理垂直滚动不再有效的问题,我改为:
为RecyclerView项目
创建一个触控侦听器mGestureDetector = new GestureDetector(itemView.getContext(), new GestureListener());
/**
* Prevents the view pager from switching tabs when scrolling the carousel
*/
private class TouchListener implements RecyclerView.OnItemTouchListener {
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
mGestureDetector.onTouchEvent(e);
return false;
}
}
实现一个GestureListener,当检测到水平滚动时不允许触摸事件:
private class GestureListener extends SimpleOnGestureListener {
private final int Y_BUFFER = 10;
@Override
public boolean onDown(MotionEvent e) {
// Prevent ViewPager from intercepting touch events as soon as a DOWN is detected.
// If we don't do this the next MOVE event may trigger the ViewPager to switch
// tabs before this view can intercept the event.
mRecyclerView.requestDisallowInterceptTouchEvent(true);
return super.onDown(e);
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (Math.abs(distanceX) > Math.abs(distanceY)) {
// Detected a horizontal scroll, prevent the viewpager from switching tabs
mRecyclerView.requestDisallowInterceptTouchEvent(true);
} else if (Math.abs(distanceY) > Y_BUFFER) {
// Detected a vertical scroll of large enough magnitude so allow the the event
// to propagate to ancestor views to allow vertical scrolling. Without the buffer
// a tab swipe would be triggered while holding finger still while glow effect was
// visible.
mRecyclerView.requestDisallowInterceptTouchEvent(false);
}
return super.onScroll(e1, e2, distanceX, distanceY);
}
}
答案 2 :(得分:0)
以防万一有人遇到与我相同的问题。
如果您在层次结构中有一个SwipeRefreshLayout
,并且其子对象确实为ViewCompat.isNestedScrollEnabled()
返回了true(在我的情况下为FrameLayout
),则由于检入{ {1}}在通过SwipeRefreshLayout
之前。
请我调试一下,但这是来自requestDisallowInterceptTouchEvent()
的特定检查:
SwipeRefreshLayout
通过删除if ((VERSION.SDK_INT >= 21 || !(this.mTarget instanceof AbsListView)) && (this.mTarget == null || ViewCompat.isNestedScrollingEnabled(this.mTarget))) {
super.requestDisallowInterceptTouchEvent(b);
}
,使FrameLayout
的孩子成为SwipeRefreshLayout
,我可以得到Shabbir Dhangot的答案。
答案 3 :(得分:0)
对于 kotlin,你可以试试这个解决方案
horizontalRecyclerView.addOnItemTouchListener(object : RecyclerView.OnItemTouchListener {
override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
val action = e.action
when (action) {
MotionEvent.ACTION_MOVE -> rv.parent.requestDisallowInterceptTouchEvent(true)
}
return false
}
override fun onTouchEvent(rv: RecyclerView, e: MotionEvent) {}
override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {}
})