I am trying to implement a horizontal recyclerview
and each item of the recyclerview
will be a vertical recyclerview
with a grid layou
I fixed this issue in a similar project by taking the opposite approach to you (and everyone else here).
Rather than allow the child to tell the parent when to stop looking at events, I let the parent decide when to ignore (based on direction). This approach requires a custom view though which can be a little more work. Below is what I created which would be used as the Outer/Parent view.
public class DirectionalRecyclerView extends RecyclerView {
private static float LOCK_DIRECTION_THRESHOLD; //The slop
private float startX;
private float startY;
private LockDirection mLockDirection = null;
public DirectionalRecyclerView(Context context) {
super(context);
findThreshold(context);
}
public DirectionalRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
findThreshold(context)
}
public DirectionalRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
findThreshold(context);
}
private void findThreshold(Context context) {
//last number is number of dp to move before deciding that's a direction not a tap, you might want to tweak it
LOCK_DIRECTION_THRESHOLD = context.getResources().getDisplayMetrics().density * 12;
}
//events start at the top of the tree and then pass down to
//each child view until they reach where they were initiated
//unless the parent (this) method returns true for this visitor
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = event.getX();
startY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
if (mLockDirection == null) {
float currentX = event.getX();
float currentY = event.getY();
float diffX = Math.abs(currentX - startX);
float diffY = Math.abs(currentY - startY);
if (diffX > LOCK_DIRECTION_THRESHOLD) {
mLockDirection = LockDirection.HORIZONTAL;
} else if (diffY > LOCK_DIRECTION_THRESHOLD) {
mLockDirection = LockDirection.VERTICAL;
}
} else {
//we have locked a direction, check whether we intercept
//the future touches in this event chain
//(returning true steals children's events, otherwise we'll
// just let the event trickle down to the child as usual)
return mLockDirection == LockDirection.HORIZONTAL;
}
break;
case MotionEvent.ACTION_UP:
mLockDirection = null;
break;
}
//dispatch cancel, clicks etc. normally
return super.onInterceptTouchEvent(event);
}
private enum LockDirection {
HORIZONTAL,
VERTICAL
}
}
Use this code to turn off scroll on recyclerview
:
recyclerView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
The problem exists in Android's implementation of the onInterceptTouchEvent() method for RecyclerView. This blog calls out the problem and fixes it as well - http://nerds.headout.com/fix-horizontal-scrolling-in-your-android-app/. The only difference is that there the parent scrolls vertically and child horizontally. But the solution takes care that it should work for your situation as well.
I used card view for cells and deactivated parent recycler view scrolling in child adapter with child cell itemView setOnClickListener
.
holder.itemView.setOnTouchListener { view, _ ->
view.parent.parent.requestDisallowInterceptTouchEvent(true)
false
}
we are calling view.parent.parent
because of the fact that itemView
is a cell layout and it's parent is our child recyclerView
and also, we need to reach child recyclerView's parent to prevent parent recyclerView
scrolling.
You should do this way:
innerRecyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
@Override
public void onTouchEvent(RecyclerView recycler, MotionEvent event) {
// Handle on touch events here
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
recycler.getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_UP:
recycler.getParent().requestDisallowInterceptTouchEvent(false);
break;
case MotionEvent.ACTION_MOVE:
recycler.getParent().requestDisallowInterceptTouchEvent(true);
break;
}
}
@Override
public boolean onInterceptTouchEvent(RecyclerView recycler, MotionEvent event) {
return false;
}
});
Hope this would help you.
Now you can try android:nestedScrollingEnabled
because Google fixed a crash with usages of nestedScrollingEnabled
(Issue 197932)