Last active
December 3, 2019 08:53
-
-
Save loukwn/237caeaf2088a55d4f939b8af7189d4c to your computer and use it in GitHub Desktop.
A recyclerview that respects the nested scrolling of its children (Translated from its kotlin version here https://medium.com/widgetlabs-engineering/scrollable-nestedscrollviews-inside-recyclerview-ca65050d828a ) Credits for the original code go to Marc Knaup.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.content.Context; | |
import android.os.Build; | |
import android.support.annotation.Nullable; | |
import android.support.v4.view.NestedScrollingParent; | |
import android.support.v7.widget.RecyclerView; | |
import android.util.AttributeSet; | |
import android.view.MotionEvent; | |
import android.view.View; | |
public class NestedRecycler extends RecyclerView implements NestedScrollingParent { | |
private View nestedScrollTarget = null; | |
private boolean nestedScrollTargetIsBeingDragged = false; | |
private boolean nestedScrollTargetWasUnableToScroll = false; | |
private boolean skipsTouchInterception = false; | |
public NestedRecycler(Context context) { | |
super(context); | |
} | |
public NestedRecycler(Context context, @Nullable AttributeSet attrs) { | |
super(context, attrs); | |
} | |
public NestedRecycler(Context context, @Nullable AttributeSet attrs, int defStyle) { | |
super(context, attrs, defStyle); | |
} | |
@Override | |
public boolean dispatchTouchEvent(MotionEvent ev) { | |
boolean temporarilySkipsInterception = nestedScrollTarget != null; | |
if (temporarilySkipsInterception) { | |
// If a descendent view is scrolling we set a flag to temporarily skip our onInterceptTouchEvent implementation | |
skipsTouchInterception = true; | |
} | |
// First dispatch, potentially skipping our onInterceptTouchEvent | |
boolean handled = super.dispatchTouchEvent(ev); | |
if (temporarilySkipsInterception) { | |
skipsTouchInterception = false; | |
// If the first dispatch yielded no result or we noticed that the descendent view is unable to scroll in the | |
// direction the user is scrolling, we dispatch once more but without skipping our onInterceptTouchEvent. | |
// Note that RecyclerView automatically cancels active touches of all its descendents once it starts scrolling | |
// so we don't have to do that. | |
if (!handled || nestedScrollTargetWasUnableToScroll) { | |
handled = super.dispatchTouchEvent(ev); | |
} | |
} | |
return handled; | |
} | |
// Skips RecyclerView's onInterceptTouchEvent if requested | |
@Override | |
public boolean onInterceptTouchEvent(MotionEvent e) { | |
return super.onInterceptTouchEvent(e) && !skipsTouchInterception; | |
} | |
@Override | |
public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) { | |
super.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed); | |
if (target == nestedScrollTarget && !nestedScrollTargetIsBeingDragged) { | |
if (dyConsumed != 0) { | |
// The descendent was actually scrolled, so we won't bother it any longer. | |
// It will receive all future events until it finished scrolling. | |
nestedScrollTargetIsBeingDragged = true; | |
nestedScrollTargetWasUnableToScroll = false; | |
} | |
else if (dyConsumed == 0 && dyUnconsumed != 0) { | |
// The descendent tried scrolling in response to touch movements but was not able to do so. | |
// We remember that in order to allow RecyclerView to take over scrolling. | |
nestedScrollTargetWasUnableToScroll = true; | |
if (target.getParent()!=null) | |
target.getParent().requestDisallowInterceptTouchEvent(false); | |
} | |
} | |
} | |
@Override | |
public void onNestedScrollAccepted(View child, View target, int axes) { | |
if (axes!=0 && View.SCROLL_AXIS_VERTICAL != 0) { | |
// A descendent started scrolling, so we'll observe it. | |
nestedScrollTarget = target; | |
nestedScrollTargetIsBeingDragged = false; | |
nestedScrollTargetWasUnableToScroll = false; | |
} | |
super.onNestedScrollAccepted(child, target, axes); | |
} | |
@Override | |
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { | |
boolean secondPart = Build.VERSION.SDK_INT < 21 || View.SCROLL_AXIS_VERTICAL != 0; | |
return (nestedScrollAxes!=0 && secondPart); | |
} | |
@Override | |
public void onStopNestedScroll(View child) { | |
nestedScrollTarget = null; | |
nestedScrollTargetIsBeingDragged = false; | |
nestedScrollTargetWasUnableToScroll = false; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment