Last active
December 10, 2017 11:12
-
-
Save mankum93/1e4deedf647567ff9baa2263cda8ac54 to your computer and use it in GitHub Desktop.
This file contains 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
/** | |
* This Behavior corrects the bug in RecyclerView that doesn't report scroll consumption | |
* properly by plugging in a scroll listener to RecyclerView that properly reports the | |
* same(on reaching the top) | |
* | |
* This is courtesy of @mak-sing on SO:(top rated answer is FLAWED!) | |
* https://stackoverflow.com/questions/30923889/flinging-with-recyclerview-appbarlayout | |
*/ | |
public final class CorrectiveAppBarLayoutFlingBehavior extends AppBarLayout.Behavior { | |
private Map<RecyclerView, RecyclerViewScrollListener> scrollListenerMap = new HashMap<>(); //keep scroll listener map, the custom scroll listener also keep the current scroll Y position. | |
public CorrectiveAppBarLayoutFlingBehavior() { | |
} | |
public CorrectiveAppBarLayoutFlingBehavior(Context context, AttributeSet attrs) { | |
super(context, attrs); | |
} | |
/** | |
* | |
* @param coordinatorLayout | |
* @param child The child that attached the behavior (AppBarLayout) | |
* @param target The scrolling target e.g. a recyclerView or NestedScrollView | |
* @param velocityX | |
* @param velocityY | |
* @param consumed The fling should be consumed by the scrolling target or not | |
* @return | |
*/ | |
@Override | |
public boolean onNestedFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY, boolean consumed) { | |
if (target instanceof RecyclerView) { | |
final RecyclerView recyclerView = (RecyclerView) target; | |
if (scrollListenerMap.get(recyclerView) == null) { | |
RecyclerViewScrollListener recyclerViewScrollListener = new RecyclerViewScrollListener(coordinatorLayout, child, this); | |
scrollListenerMap.put(recyclerView, recyclerViewScrollListener); | |
recyclerView.addOnScrollListener(recyclerViewScrollListener); | |
} | |
scrollListenerMap.get(recyclerView).setVelocity(velocityY); | |
consumed = scrollListenerMap.get(recyclerView).getScrolledY() > 0; //recyclerView only consume the fling when it's not scrolled to the top | |
} | |
return super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed); | |
} | |
private static class RecyclerViewScrollListener extends RecyclerView.OnScrollListener { | |
private int scrolledY; | |
private int scrollState; | |
private float velocity; | |
private WeakReference<CoordinatorLayout> coordinatorLayoutRef; | |
private WeakReference<AppBarLayout> childRef; | |
private WeakReference<CorrectiveAppBarLayoutFlingBehavior> behaviorWeakReference; | |
public RecyclerViewScrollListener(CoordinatorLayout coordinatorLayout, AppBarLayout child, CorrectiveAppBarLayoutFlingBehavior barBehavior) { | |
coordinatorLayoutRef = new WeakReference<CoordinatorLayout>(coordinatorLayout); | |
childRef = new WeakReference<AppBarLayout>(child); | |
behaviorWeakReference = new WeakReference<CorrectiveAppBarLayoutFlingBehavior>(barBehavior); | |
} | |
@Override | |
public void onScrollStateChanged(RecyclerView recyclerView, int newState) { | |
scrollState = newState; | |
} | |
public void setVelocity(float velocity) { | |
this.velocity = velocity; | |
} | |
public int getScrolledY() { | |
return scrolledY; | |
} | |
@Override | |
public void onScrolled(RecyclerView recyclerView, int dx, int dy) { | |
scrolledY += dy; | |
if (scrolledY <= 0 && (scrollState != RecyclerView.SCROLL_STATE_IDLE) | |
&& childRef.get() != null && | |
coordinatorLayoutRef.get() != null && | |
behaviorWeakReference.get() != null) { | |
if(scrollState == RecyclerView.SCROLL_STATE_SETTLING){ | |
// If the RecyclerView is settling on reaching top then we don't need | |
// a fling with full velocity, just a scroll. We do a fling faking a scroll | |
velocity = velocity * 0.35f; | |
} | |
//manually trigger the fling when it's scrolled at the top | |
behaviorWeakReference.get().onNestedFling(coordinatorLayoutRef.get(), childRef.get(), recyclerView, 0, velocity, false); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment