CollapsingToolbarLayout折叠后Fling滚动中断问题求助
我明白你遇到的这个Fling滚动中断的问题了——当你快速滑动CollapsingToolbarLayout完成折叠后,滚动没法继续传递给下方的NestedScrollView,确实挺影响用户体验的。这是因为默认的AppBarLayout.Behavior在处理Fling动作时,当AppBarLayout滚动到完全折叠的边界后,没有把剩余的滚动动量传递给下方的滚动子View。下面给你几个可行的解决思路:
方案一:自定义AppBarLayout Behavior(最可靠的方案)
我们可以重写AppBarLayout.Behavior的相关方法,当AppBar已经无法继续滚动时,主动把Fling的动量传递给NestedScrollView。
1. 新建自定义Behavior类
创建一个名为AppBarLayoutFlingBehavior的类,代码如下:
import android.content.Context; import android.util.AttributeSet; import android.view.View; import androidx.coordinatorlayout.widget.CoordinatorLayout; import com.google.android.material.appbar.AppBarLayout; import androidx.core.view.ViewCompat; public class AppBarLayoutFlingBehavior extends AppBarLayout.Behavior { public AppBarLayoutFlingBehavior() { super(); } public AppBarLayoutFlingBehavior(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean onNestedFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY, boolean consumed) { // 当AppBar已经折叠到最小高度,且是向下滚动的Fling,让NestedScrollView接手剩余动量 if (velocityY > 0 && child.getBottom() <= getCollapsedAppBarHeight(child)) { ViewCompat.postOnAnimation(target, () -> ViewCompat.fling(target, 0, (int) velocityY)); return false; } return super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed); } @Override public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY) { // AppBar已经折叠时,允许NestedScrollView处理Fling if (velocityY > 0 && child.getBottom() <= getCollapsedAppBarHeight(child)) { return false; } return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY); } // 获取AppBar折叠后的最小高度(也就是Toolbar的高度) private int getCollapsedAppBarHeight(AppBarLayout appBarLayout) { int minHeight = appBarLayout.getMinimumHeight(); for (int i = 0; i < appBarLayout.getChildCount(); i++) { View child = appBarLayout.getChildAt(i); if (child instanceof com.google.android.material.appbar.CollapsingToolbarLayout) { minHeight = Math.max(minHeight, ((com.google.android.material.appbar.CollapsingToolbarLayout) child).getScrimVisibleHeightTrigger()); } } return minHeight; } }
2. 在布局中应用这个Behavior
修改你的AppBarLayout标签,添加自定义Behavior的引用:
<com.google.android.material.appbar.AppBarLayout android:id="@+id/app_bar" android:layout_width="match_parent" android:layout_height="400dp" android:fitsSystemWindows="true" android:theme="@style/AppTheme.AppBarOverlay" app:layout_behavior="com.journaldev.collapsingtoolbarlayout.AppBarLayoutFlingBehavior"> <!-- 原来的CollapsingToolbarLayout等内容保持不变 --> </com.google.android.material.appbar.AppBarLayout>
方案二:升级Material Components库版本
你当前使用的com.google.android.material:material:1.1.0-alpha04是一个早期的测试版本,存在不少嵌套滚动的bug。建议升级到稳定版本,比如:
implementation "com.google.android.material:material:1.4.0"
升级后记得同步Gradle,同时把compileSdkVersion调整到30或更高(Material 1.4.0要求最低compileSdk 30),这样默认的Behavior可能已经修复了这个Fling传递的问题,不需要额外写自定义代码。
方案三:自定义NestedScrollView的Behavior(补充方案)
如果上面的方案都不生效,可以尝试自定义AppBarScrollingViewBehavior,确保它能接收来自AppBar的剩余滚动动量:
import android.content.Context; import android.util.AttributeSet; import androidx.coordinatorlayout.widget.CoordinatorLayout; import com.google.android.material.appbar.AppBarLayout; import androidx.core.widget.NestedScrollView; public class NestedScrollViewFlingBehavior extends AppBarLayout.ScrollingViewBehavior { public NestedScrollViewFlingBehavior() { super(); } public NestedScrollViewFlingBehavior(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean onNestedFling(CoordinatorLayout coordinatorLayout, NestedScrollView child, View target, float velocityX, float velocityY, boolean consumed) { // 如果AppBar没有消费这个Fling,让NestedScrollView自己处理 if (!consumed) { child.fling((int) velocityY); return true; } return super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed); } }
然后在NestedScrollView中应用这个Behavior:
<androidx.core.widget.NestedScrollView android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="com.journaldev.collapsingtoolbarlayout.NestedScrollViewFlingBehavior"> <!-- 原来的TextView内容保持不变 --> </androidx.core.widget.NestedScrollView>
完成修改后,重新运行App,快速滑动CollapsingToolbarLayout的图片区域,当Toolbar折叠完成后,应该会自动继续滚动NestedScrollView里的文本内容,和从NestedScrollView发起Fling的体验完全一致。
内容的提问来源于stack exchange,提问作者Murat

