I am using the new CoordinatorLayout with AppBarLayout and CollapsingToolbarLayout. Below AppBarLayout, I have a RecyclerView with a list of content.
I have verified that fling scrolling works on the RecyclerView when I am scrolling up and down the list. However, I would also like the AppBarLayout to smoothly scroll during expansion.
When scrolling up to expand the CollaspingToolbarLayout, scrolling immediately stops once lifting your finger off the screen. If you scroll up in a quick motion, sometimes the CollapsingToolbarLayout re-collapses as well. This behavior with the RecyclerView seems to function much differently than when using a NestedScrollView.
I've tried to set different scroll properties on the recyclerview but I haven't been able to figure this out.
Here is a video showing some of the scrolling issues. https://youtu.be/xMLKoJOsTAM
Here is an example showing the issue with the RecyclerView (CheeseDetailActivity). https://github.com/tylerjroach/cheesesquare
Here is the original example that uses a NestedScrollView from Chris Banes. https://github.com/chrisbanes/cheesesquare
The answer of Kirill Boyarshinov was almost correct.
The main problem is that the RecyclerView sometimes is giving incorrect fling direction, so if you add the following code to his answer it works correctly:
public final class FlingBehavior extends AppBarLayout.Behavior {
private static final int TOP_CHILD_FLING_THRESHOLD = 3;
private boolean isPositive;
public FlingBehavior() {
}
public FlingBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onNestedFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY, boolean consumed) {
if (velocityY > 0 && !isPositive || velocityY < 0 && isPositive) {
velocityY = velocityY * -1;
}
if (target instanceof RecyclerView && velocityY < 0) {
final RecyclerView recyclerView = (RecyclerView) target;
final View firstChild = recyclerView.getChildAt(0);
final int childAdapterPosition = recyclerView.getChildAdapterPosition(firstChild);
consumed = childAdapterPosition > TOP_CHILD_FLING_THRESHOLD;
}
return super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed);
}
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dx, int dy, int[] consumed) {
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
isPositive = dy > 0;
}
}
I hope that this helps.
Seems that v23
update did not fix it yet.
I have found sort of of hack to fix it with flinging down. The trick is to reconsume fling event if ScrollingView's top child is close to the beginning of data in Adapter.
public final class FlingBehavior extends AppBarLayout.Behavior {
public FlingBehavior() {
}
public FlingBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onNestedFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY, boolean consumed) {
if (target instanceof ScrollingView) {
final ScrollingView scrollingView = (ScrollingView) target;
consumed = velocityY > 0 || scrollingView.computeVerticalScrollOffset() > 0;
}
return super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed);
}
}
Use it in your layout like that:
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="your.package.FlingBehavior">
<!--your views here-->
</android.support.design.widget.AppBarLayout>
EDIT: Fling event reconsuming is now based on verticalScrollOffset
instead of amount of items on from top of RecyclerView
.
EDIT2: Check target as ScrollingView
interface instance instead of RecyclerView
. Both RecyclerView
and NestedScrollingView
implement it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With