Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lollipop AppBarLayout/Toolbar missing overscroll animation

using the most basic example with AppBarLayout and Toolbar, I cannot see the overscroll animation (the glow from bottom nor top) when trying to scroll more. However, if you fling the content, it will show it.

Here is the code (nav_drawer_toolbar_layout.xml):

<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- Replace fragments in this content frame, like a RecycleView -->
    <FrameLayout
        android:id="@+id/content_frame"
        app:layout_behavior="android.support.design.widget.AppBarLayout$ScrollingViewBehavior"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:minHeight="?attr/actionBarSize"
            app:titleTextAppearance="@style/Base.TextAppearance.AppCompat.Title"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
            app:layout_scrollFlags="scroll|enterAlways"/>
    </android.support.design.widget.AppBarLayout>

</android.support.design.widget.CoordinatorLayout>

Followed by simple Activity class:

public class MyActivity extends AppCompatActivity implements {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.nav_drawer_toolbar_layout);

        // Setup the toolbar/actionbar
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FragmentManager manager = getFragmentManager();
        manager.beginTransaction().replace(R.id.content_frame, new MyFragmentList).commit();
    }
}

MyFragmentList is a fragment with a RecycleView with content to scroll the app.

However if I remove AppBarLayout from the xml and leave Toolbar open (just comment AppBarLayout opening and closing), it will show the overscroll animation (the glow) when scrolling.

Or if you remove layout_scrollFlags="scroll" then the overscroll works but you can't get the actionbar to hide when you scroll.

For extra information, debugging RecycleView, line 2272

if(this.mBottomGlow != null && !this.mBottomGlow.isFinished()) {

is always finished when including AppBarLayout and not finished when it is not there. Is something over-writing its touch events?

Does anyone know who to show overscroll animation (glow) with AppBarLayout?

like image 630
user654628 Avatar asked Sep 19 '15 10:09

user654628


1 Answers

EDIT: There seems to be a ticket for this bug. You could definintely do what [email protected] did and extend RecyclerView to override RecyclerView#dispatchNestedScroll to always return false (he writes true in his report) you can get overscroll animations working, though i'm pretty sure it might break something down the line.

Unfortunately how RecyclerView is coded and how NestedScrollingChild API is made there is no clean way to have the desired behavior.

This is from RecyclerView (23.1.1, however I do not believe any version before it fixes the issue) inside the method scrollByInternal.

if (dispatchNestedScroll(consumedX, consumedY, unconsumedX, unconsumedY, mScrollOffset)) {
    // Update the last touch co-ords, taking any scroll offset into account
    mLastTouchX -= mScrollOffset[0];
    mLastTouchY -= mScrollOffset[1];
    if (ev != null) {
        ev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
    }
    mNestedOffsets[0] += mScrollOffset[0];
    mNestedOffsets[1] += mScrollOffset[1];
} else if (ViewCompat.getOverScrollMode(this) != ViewCompat.OVER_SCROLL_NEVER) {
    if (ev != null) {
        pullGlows(ev.getX(), unconsumedX, ev.getY(), unconsumedY);
    }
    considerReleasingGlowsOnScroll(x, y);
}

As we can see here on the javadoc for dispatchNestedScroll (part of the NestedScrollingChild API) as long as there is one parent that consumes the scroll, RecyclerView will not apply any overscroll animation (edge glow).

AppBarLayout does consume scrolling, more specifically as long as there is a NestedScrollingParent that returns true on onStartNestedScroll, overscroll animations will not happen.

CoordinatorLayout is a NestedScrollingParent but does not return true unless there is a CoordinatorLayout.Behavior that does. AppBarLayout's default behavior does implement this methdo to return true when there is vertical scrolling + AppBarLayout has something to scroll + view is big enough to scroll.

// Return true if we're nested scrolling vertically, and we have scrollable children
// and the scrolling view is big enough to scroll
final boolean started = (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0
            && child.hasScrollableChildren()
            && parent.getHeight() - directTargetChild.getHeight() <= child.getHeight();

Flinging takes a slightly different approach, allowing the overscroll animation happening regardless if the NestedScrollingParent is consuming the scrolling.

if (!dispatchNestedPreFling(velocityX, velocityY)) {
    final boolean canScroll = canScrollHorizontal || canScrollVertical;
    dispatchNestedFling(velocityX, velocityY, canScroll);

    if (canScroll) {
        velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
        velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
        mViewFlinger.fling(velocityX, velocityY);
        return true;
    }
}

Honestly I cannot tell if this is a bug because both logic make sense. If you are scrolling to the top part of the view, and you have something akin to a CollapsingToolbar, you wouldn't want an overscoll animation to happen. However there is a way to make it so that the behavior can consume the x/y amount of scrolling to stop the animation from happening. It is also weird that both code for scrolling and flinging is different.

like image 108
David Park Avatar answered Oct 19 '22 05:10

David Park