问题
I have a viewpager and within one of the fragments I have two separate fragments containing a vertical and a horizontal recyclerview respectively.
When I scroll the horizontal recyclerview to the last item and try to swipe further, the viewpager scrolls to the next page. I do not want this to happen. I want to disable the viewpager's paging when I try to overscroll the horizontal recyclerview.
However, I do not want to disable the viewpager's paging when I swipe anywhere else. For example, If I were to swipe on the vertical recyclerview or any empty space within the parent fragment, it should still cause the viewpager to change pages.
I read in this SO question, how to disable paging of the viewpager. Also this SO question is similar in that there is a child viewpager, however I have not had success trying to replicate that with a horizontal recyclerview.
Here's some structure:
The custom viewpager which allows me to disable paging(took it from first SO link above):
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;
} }
For the horizontal recyclerview I set an ontouchlistener(similar to second SO link above):
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;
}
});
Extra observations: I noticed that sometimes if I long press the horizontal recyclerview before I swipe it will not go to the next page of the viewpager. However, if I swipe quickly the viewpager will go to the next page.
Does anyone know the proper way to do this?
Any help is appreciated.
回答1:
Here is solution.
requestDisallowInterceptTouchEvent()
This method will not allow parent to move. It stops viewpager touch event.
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) {
}
});
回答2:
In order to handle the issue with vertical scroll no longer working I did this instead:
Create a touch listener for the RecyclerView item
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;
}
}
And implement a GestureListener that disallows touch events when horizontal scrolls are detected:
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);
}
}
回答3:
Just in case anyone has the same problem I have.
If you have a SwipeRefreshLayout in your hierarchy and its child does return true for ViewCompat.isNestedScrollEnabled() (in my case a FrameLayout), this won't work because of a check in SwipeRefreshLayout before it passes on the requestDisallowInterceptTouchEvent().
Took me a bit to debug, but here is the specific check from SwipeRefreshLayout:
if ((VERSION.SDK_INT >= 21 || !(this.mTarget instanceof AbsListView)) && (this.mTarget == null || ViewCompat.isNestedScrollingEnabled(this.mTarget))) {
super.requestDisallowInterceptTouchEvent(b);
}
By removing the FrameLayout, making SwipeRefreshLayout's child a NestedScrollView, I was able to get Shabbir Dhangot's answer to work.
来源:https://stackoverflow.com/questions/38466413/disable-viewpager-paging-when-child-recyclerview-scrolls-to-last-item