Overscroll AppBarLayout Behavior

Nikola Despotoski
Nov 19, 2015 · 3 min read

Unfortunately, Youtube Music app is not available in my country and I tried to get apk from various piracy sites, but still I wasn’t able to look what’s going on in this app. Thanks to this redditor, who have opened a thread in /r/materialdesign and posted videos captures of Youtube Music app on my request I was able to see the behavior.

Image for post
Image for post
Actual YT Music app; Probably a Behavior

From what I could see, my first guess that the album art is inside a AppBarLayout and scaled when the content below overscrolls. Let’s assume this assumption is correct and attempt to express it in terms of Behavior. IMHO, if my assumptions are true, Google should include overscroll guidelines and specs in their Scrolling Techniques section of MD guidelines.

Our goal is to keep the AppBarLayout.Behavior intact and create our extended behavior on top of it. Therefore:

public class OverscrollScalingViewAppBarLayoutBehavior extends AppBarLayout.ScrollingViewBehavior

As it is default AppBarLayout.Behavior and suggests, we need to react only when our dependency view is AppBarLayout. No biggie:

public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
return dependency instanceof AppBarLayout;

Next we need to obtain instance of the View that we are going to scale on overscroll. Best way is to do this is in onLayoutChild() method:

public boolean onLayoutChild(CoordinatorLayout parent ....) {
boolean superLayout = super.onLayoutChild(parent, abl, layoutDirection);
if (mTargetScalingView == null) {
mTargetScalingView = parent.findViewByTag(TAG);
if(mTargetScalingView != null){
return superLayout;

But also we need to react on vertical scrolling:

public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout,... int nestedScrollAxes) {
return nestedScrollAxes == View.SCROLL_AXIS_VERTICAL;

Let’s set ViewScaler as our default Scaler, if we haven’t set it programatically, previously.

Really important question is knowing the moment when content is overscrolling. Genuine CoordinatorLayout.Behavior offers a method named onNestedScroll(). This method is called when scrolling is ongoing, but also when we have overscroll. Last two parameters dyUnconsumed and dxUnconsumed, give us the amount of pixels that weren’t consumed by the target view of the behavior.

This method is really important for us to perform the scaling. So let’s list the cases when we should scale and when we should not:

Scale when:

1. There is unconsumed pixels, i.e dyUnconsumed < 0

2. AppBarLayout is expanded, getTopAndBottomOffset() >= mScaleImpl.getInitialParentBottom()

Don’t scale when:

1. We don’t have view to scale, which is child of AppBarLayout
2. There are consumed pixels, i.e dyConsumed != 0

public void onNestedScroll(CoordinatorLayout ... int dxUnconsumed, int dyUnconsumed) {
if (mTargetScalingView == null || dyConsumed != 0) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);

if (dyUnconsumed < 0 && getTopAndBottomOffset() >= mScaleImpl.getInitialParentBottom()) {
int absDyUnconsumed = Math.abs(dyUnconsumed);
mTotalDyUnconsumed += absDyUnconsumed;
mTotalDyUnconsumed = Math.min(mTotalDyUnconsumed, mTotalTargetDyUnconsumed);
} else {
mTotalDyUnconsumed = 0;
if (dyConsumed != 0) {
super.onNestedScroll(coordinatorLayout, .... dxUnconsumed, dyUnconsumed);

When the nested overscroll has ended, we need to reset views to their original bounds and scales:

public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target) {
super.onStopNestedScroll(coordinatorLayout, child, target);



This class implements the logic how AppBarLayout should change its bottom and how view should scale. Mostly this behavior relies on cumulative unconsumed pixels. We can set bound value for maximum cumulative value and easily find how we are going to change AppBarLayout bottom and scale the target view. ParentScaler class which is superclass of ViewScaler is doing the (almost) smooth AppBarLayout expansion and retraction. I’ll spare you by not clog the post with code. If you are interested, you can grab entire code here.


For the geeks, there is MatrixScaler private class, which I haven’t time to finish it. This class is supposed scale the image Matrix, if the view that suppose to scale is ImageView with ScaleType.MATRIX.


Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store