Android CoordinatorLayout 与 AppbarLayout Double Fling Bug

Android CoordinatorLayout with AppbarLayout Double Fling Bug

A​​ppbarLayout 有一个很大的child,所以我们可以拖拽它。

当我先用 child 抛出 appbar 并在之前 抛出 scrollview 时出错,之前的抛出动画是 finished

当 appbar 和 nestedscrollview 都发生双重滚动时,滚动变得一团糟

这里是layout.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_behavior="com.google.android.material.appbar.AppBarLayout$Behavior"
        >
        <View
            android:layout_width="match_parent"
            android:layout_height="800dp"
            android:background="#8f8"
            app:layout_scrollFlags="scroll"
            />
    </com.google.android.material.appbar.AppBarLayout>
    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <View
                android:layout_width="match_parent"
                android:layout_height="800dp"
                android:background="#88f" />
        </LinearLayout>
    </androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

我和RecyclerView

有同样的问题

HeaderViewBehavior.setHeaderTopBottomOffsetnewOffset参数调用方向不同。 当 onStartNestedScroll 被调用时,你应该取消 AppBarLayout.Behavior fling。

如何修复:

覆盖AppBarLayout.Behavior

package com.google.android.material.appbar // original package

import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.widget.OverScroller
import androidx.coordinatorlayout.widget.CoordinatorLayout

class FixFlingBehavior @JvmOverloads constructor(
    context: Context,
    attributeSet: AttributeSet? = null
) : AppBarLayout.Behavior(context, attributeSet) {

    private var isAppbarFlinging: Boolean = false
    private var originalScroller: OverScroller? = null
    private val fakeScroller: OverScroller = object : OverScroller(context) {
        override fun computeScrollOffset(): Boolean {
            scroller = originalScroller
            return false // it cancels HeaderBehavior FlingRunnable
        }
    }

    override fun setHeaderTopBottomOffset(coordinatorLayout: CoordinatorLayout, appBarLayout: AppBarLayout, newOffset: Int, minOffset: Int, maxOffset: Int): Int {
        isAppbarFlinging = minOffset == Int.MIN_VALUE && maxOffset == Int.MAX_VALUE
        if (scroller === fakeScroller) {
            scroller = originalScroller
        }
        return super.setHeaderTopBottomOffset(coordinatorLayout, appBarLayout, newOffset, minOffset, maxOffset)
    }

    override fun onFlingFinished(parent: CoordinatorLayout, layout: AppBarLayout) {
        super.onFlingFinished(parent, layout)
        isAppbarFlinging = false
    }

    override fun onStartNestedScroll(parent: CoordinatorLayout, child: AppBarLayout, directTargetChild: View, target: View, nestedScrollAxes: Int, type: Int): Boolean {
        if (isAppbarFlinging && scroller !== fakeScroller) {
            originalScroller = scroller
            scroller = fakeScroller
        }
        return super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes, type)
    }
}

并在您的布局中使用它

...
    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_behavior="com.google.android.material.appbar.FixFlingBehavior">
...