用RecyclerView + AppBarLayout投掷

我正在使用AppBarLayout和CollapsingToolbarLayout的新的CoordinatorLayout。 在AppBarLayout下面,我有一个带有内容列表的RecyclerView。

我已经validation了当我在列表中上下滚动时,在RecyclerView上的投掷滚动工作。 不过,我也希望AppBarLayout在扩展期间平滑滚动。

当向上滚动展开CollaspingToolbarLayout时,一旦将手指从屏幕上移开,滚动立即停止。 如果快速向上滚动,则有时CollapsingToolbarLayout也会重新崩溃。 RecyclerView的这种行为似乎与使用NestedScrollView时的function大不相同。

我试图在recyclerview上设置不同的滚动属性,但我一直无法弄清楚这一点。

这是一个显示一些滚动问题的video。 https://youtu.be/xMLKoJOsTAM

下面是一个显示RecyclerView(CheeseDetailActivity)问题的例子。 https://github.com/tylerjroach/cheesesquare

这里是使用Chris Banes的NestedScrollView的原始示例。 https://github.com/chrisbanes/cheesesquare

Kirill Boyarshinov的答案几乎是正确的。

主要的问题是RecyclerView有时会给出不正确的方向,所以如果你把下面的代码添加到他的回答中,它可以正常工作:

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; } } 

我希望这个对你有用。

似乎v23更新没有解决它呢。

我已经发现了一些破解,以消除它。 诀窍是如果ScrollingView的顶部的孩子接近适配器中的数据的开始,则重新build立滑动事件。

 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); } } 

在你的布局中使用它:

  <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> 

编辑: Fling事件reconsuming现在基于verticalScrollOffset而不是从RecyclerView顶部的项目数量。

编辑2:检查目标为ScrollingView接口实例,而不是RecyclerViewRecyclerViewNestedScrollingView实现它。

我已经通过将OnScrollingListener应用于recyclerView来find修复。 现在它工作得很好。 问题是recyclerview提供了错误的消费价值,并且行为不知道什么时候回收者视图滚动到顶部。

 package com.singmak.uitechniques.util.coordinatorlayout; import android.content.Context; import android.support.design.widget.AppBarLayout; import android.support.design.widget.CoordinatorLayout; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.view.View; import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.Map; /** * Created by maksing on 26/3/2016. */ public final class RecyclerViewAppBarBehavior extends AppBarLayout.Behavior { private Map<RecyclerView, RecyclerViewScrollListener> scrollListenerMap = new HashMap<>(); //keep scroll listener map, the custom scroll listener also keep the current scroll Y position. public RecyclerViewAppBarBehavior() { } public RecyclerViewAppBarBehavior(Context context, AttributeSet attrs) { super(context, attrs); } /** * * @param coordinatorLayout * @param child The child that attached the behavior (AppBarLayout) * @param target The scrolling target eg a recyclerView or NestedScrollView * @param velocityX * @param velocityY * @param consumed The fling should be consumed by the scrolling target or not * @return */ @Override public boolean onNestedFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY, boolean consumed) { if (target instanceof RecyclerView) { final RecyclerView recyclerView = (RecyclerView) target; if (scrollListenerMap.get(recyclerView) == null) { RecyclerViewScrollListener recyclerViewScrollListener = new RecyclerViewScrollListener(coordinatorLayout, child, this); scrollListenerMap.put(recyclerView, recyclerViewScrollListener); recyclerView.addOnScrollListener(recyclerViewScrollListener); } scrollListenerMap.get(recyclerView).setVelocity(velocityY); consumed = scrollListenerMap.get(recyclerView).getScrolledY() > 0; //recyclerView only consume the fling when it's not scrolled to the top } return super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed); } private static class RecyclerViewScrollListener extends RecyclerView.OnScrollListener { private int scrolledY; private boolean dragging; private float velocity; private WeakReference<CoordinatorLayout> coordinatorLayoutRef; private WeakReference<AppBarLayout> childRef; private WeakReference<RecyclerViewAppBarBehavior> behaviorWeakReference; public RecyclerViewScrollListener(CoordinatorLayout coordinatorLayout, AppBarLayout child, RecyclerViewAppBarBehavior barBehavior) { coordinatorLayoutRef = new WeakReference<CoordinatorLayout>(coordinatorLayout); childRef = new WeakReference<AppBarLayout>(child); behaviorWeakReference = new WeakReference<RecyclerViewAppBarBehavior>(barBehavior); } @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { dragging = newState == RecyclerView.SCROLL_STATE_DRAGGING; } public void setVelocity(float velocity) { this.velocity = velocity; } public int getScrolledY() { return scrolledY; } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { scrolledY += dy; if (scrolledY <= 0 && !dragging && childRef.get() != null && coordinatorLayoutRef.get() != null && behaviorWeakReference.get() != null) { //manually trigger the fling when it's scrolled at the top behaviorWeakReference.get().onNestedFling(coordinatorLayoutRef.get(), childRef.get(), recyclerView, 0, velocity, false); } } } } 

自从支持devise26.0.0以来,它已经被修复了。

 compile 'com.android.support:design:26.0.0' 

这是Google支持deviseAppBarLayout的stream畅版本。 如果你使用AppBarLayout,你会知道它有一个问题。

 compile "me.henrytao:smooth-app-bar-layout:<latest-version>" 

在这里看到图书馆.. https://github.com/henrytao-me/smooth-app-bar-layout

这是一个回收查看错误。 它应该在v23.1.0中修复。

看看https://code.google.com/p/android/issues/detail?id=177729

在这里输入图像说明

这是我的布局和滚动它的工作,因为它应该。

 <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:id="@+id/container"> <android.support.design.widget.AppBarLayout android:id="@+id/appbarLayout" android:layout_height="192dp" android:layout_width="match_parent"> <android.support.design.widget.CollapsingToolbarLayout android:id="@+id/ctlLayout" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_scrollFlags="scroll|exitUntilCollapsed" app:contentScrim="?attr/colorPrimary" app:layout_collapseMode="parallax"> <android.support.v7.widget.Toolbar android:id="@+id/appbar" android:layout_height="?attr/actionBarSize" android:layout_width="match_parent" app:layout_scrollFlags="scroll|enterAlways" app:layout_collapseMode="pin"/> </android.support.design.widget.CollapsingToolbarLayout> </android.support.design.widget.AppBarLayout> <android.support.v7.widget.RecyclerView android:id="@+id/catalogueRV" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"/> </android.support.design.widget.CoordinatorLayout> 

我的解决scheme迄今为止,基于麦星和马诺洛·加西亚的回答。

这并不完全。 现在我不知道如何重新计算valide速度以避免奇怪的效果:appbar可以比滚动速度更快地扩展。 但无法联系扩展appbar和滚动回收站视图的状态。

 import android.content.Context; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.design.widget.AppBarLayout; import android.support.design.widget.CoordinatorLayout; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.view.View; import java.lang.ref.WeakReference; public class FlingAppBarLayoutBehavior extends AppBarLayout.Behavior { // The minimum I have seen for a dy, after the recycler view stopped. private static final int MINIMUM_DELTA_Y = -4; @Nullable RecyclerViewScrollListener mScrollListener; private boolean isPositive; public FlingAppBarLayoutBehavior() { } public FlingAppBarLayoutBehavior(Context context, AttributeSet attrs) { super(context, attrs); } public boolean callSuperOnNestedFling( CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY, boolean consumed) { return super.onNestedFling( coordinatorLayout, child, target, velocityX, velocityY, consumed ); } @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) { RecyclerView recyclerView = (RecyclerView) target; if (mScrollListener == null) { mScrollListener = new RecyclerViewScrollListener( coordinatorLayout, child, this ); recyclerView.addOnScrollListener(mScrollListener); } mScrollListener.setVelocity(velocityY); } 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; } private static class RecyclerViewScrollListener extends RecyclerView.OnScrollListener { @NonNull private final WeakReference<AppBarLayout> mAppBarLayoutWeakReference; @NonNull private final WeakReference<FlingAppBarLayoutBehavior> mBehaviorWeakReference; @NonNull private final WeakReference<CoordinatorLayout> mCoordinatorLayoutWeakReference; private int mDy; private float mVelocity; public RecyclerViewScrollListener( @NonNull CoordinatorLayout coordinatorLayout, @NonNull AppBarLayout child, @NonNull FlingAppBarLayoutBehavior barBehavior) { mCoordinatorLayoutWeakReference = new WeakReference<>(coordinatorLayout); mAppBarLayoutWeakReference = new WeakReference<>(child); mBehaviorWeakReference = new WeakReference<>(barBehavior); } @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { if (newState == RecyclerView.SCROLL_STATE_IDLE) { if (mDy < MINIMUM_DELTA_Y && mAppBarLayoutWeakReference.get() != null && mCoordinatorLayoutWeakReference.get() != null && mBehaviorWeakReference.get() != null) { // manually trigger the fling when it's scrolled at the top mBehaviorWeakReference.get() .callSuperOnNestedFling( mCoordinatorLayoutWeakReference.get(), mAppBarLayoutWeakReference.get(), recyclerView, 0, mVelocity, // TODO find a way to recalculate a correct velocity. false ); } } } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { mDy = dy; } public void setVelocity(float velocity) { mVelocity = velocity; } } } 

我在AppBarLayout里添加一个1dp高度的视图,然后效果会好很多。 这是我的布局。

  <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/white" tools:context="com.spof.spof.app.UserBeachesActivity"> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <android.support.v7.widget.Toolbar android:id="@+id/user_beaches_toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:layout_alignParentTop="true" android:background="?attr/colorPrimary" android:minHeight="?attr/actionBarSize" android:theme="@style/WhiteTextToolBar" app:layout_scrollFlags="scroll|enterAlways" /> <View android:layout_width="match_parent" android:layout_height="1dp" /> </android.support.design.widget.AppBarLayout> <android.support.v7.widget.RecyclerView android:id="@+id/user_beaches_rv" android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_behavior="@string/appbar_scrolling_view_behavior" /> 

在我的情况下,我得到的问题扔在RecyclerView不会顺利滚动,使其卡住。

这是因为,由于某种原因, 我忘记了我把我的RecyclerView放在一个NestedScrollView

这是一个愚蠢的错误,但我花了一段时间才弄清楚了…

接受的答案不适合我,因为我有一个SwipeRefreshLayoutViewPager 。 这是在层次结构中寻找RecyclerView的改进版本,可用于任何布局:

 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) { RecyclerView recycler = findRecycler((ViewGroup) target); if (recycler != null){ target = recycler; } } 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; } @Nullable private RecyclerView findRecycler(ViewGroup container){ for (int i = 0; i < container.getChildCount(); i++) { View childAt = container.getChildAt(i); if (childAt instanceof RecyclerView){ return (RecyclerView) childAt; } if (childAt instanceof ViewGroup){ return findRecycler((ViewGroup) childAt); } } return null; } } 

这里已经有一些非常受欢迎的解决scheme,但在与他们一起玩之后,我想出了一个相当简单的解决scheme,对我来说非常合适。 我的解决scheme还确保了AppBarLayout只有在可滚动内容达到顶端AppBarLayout被展开,这是其他解决scheme的优势。

 private int mScrolled; private int mPreviousDy; private AppBarLayout mAppBar; myRecyclerView.addOnScrollListener(new OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); mScrolled += dy; // scrolled to the top with a little more velocity than a slow scroll eg flick/fling. // Adjust 10 (vertical change of event) as you feel fit for you requirement if(mScrolled == 0 && dy < -10 && mPrevDy < 0) { mAppBar.setExpanded(true, true); } mPreviousDy = dy; }); 

因为上面的答案不是完全满足我的需要,或者工作得不好,所以在这里增加另一个答案。 这一部分是基于这里传播的想法。

那么这个是做什么的?

场景向下飞扬:如果AppBarLayout被折叠,它可以让RecyclerView自己投掷而不做任何事情。 否则,它会崩溃AppBarLayout,并阻止RecyclerView的一举一动。 一旦它被折叠(达到给定的速度要求的点),并且如果有速度剩下的话,RecyclerView就会以原来的速度减去App​​BarLayout刚刚消耗掉的东西。

场景向上跳动:如果RecyclerView的滚动偏移量不为零,则会以原始速度抛出。 一旦完成,如果仍然有速度(即RecyclerView滚动到位置0),AppBarLayout得到扩大,直到原来的速度减去正好消耗的需求。 否则,AppBarLayout会扩展到原始速度要求的地步。

AFAIK,这是隐瞒的行为。

涉及到很多反思,这很自定义。 没有发现问题,但。 它也写在Kotlin,但理解它应该是没有问题的。 您可以使用IntelliJ Kotlin插件将其编译为字节码 – >并将其反编译回Java。 要使用它,将其放置在android.support.v7.widget包中,并将其设置为AppBarLayout的CoordinatorLayout.LayoutParams在代码中的行为(或添加xml适用的构造函数或其他)

让我知道,如果它帮助你:D

 /* * Copyright 2017 Julian Ostarek * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.support.v7.widget import android.support.design.widget.AppBarLayout import android.support.design.widget.CoordinatorLayout import android.support.v4.widget.ScrollerCompat import android.view.View import android.widget.OverScroller class SmoothScrollBehavior(recyclerView: RecyclerView) : AppBarLayout.Behavior() { // We're using this SplineOverScroller from deep inside the RecyclerView to calculate the fling distances private val splineOverScroller: Any private var isPositive = false init { val scrollerCompat = RecyclerView.ViewFlinger::class.java.getDeclaredField("mScroller").apply { isAccessible = true }.get(recyclerView.mViewFlinger) val overScroller = ScrollerCompat::class.java.getDeclaredField("mScroller").apply { isAccessible = true }.get(scrollerCompat) splineOverScroller = OverScroller::class.java.getDeclaredField("mScrollerY").apply { isAccessible = true }.get(overScroller) } override fun onNestedFling(coordinatorLayout: CoordinatorLayout?, child: AppBarLayout, target: View?, velocityX: Float, givenVelocity: Float, consumed: Boolean): Boolean { // Making sure the velocity has the correct sign (seems to be an issue) var velocityY: Float if (isPositive != givenVelocity > 0) { velocityY = givenVelocity * - 1 } else velocityY = givenVelocity if (velocityY < 0) { // Decrement the velocity to the maximum velocity if necessary (in a negative sense) velocityY = Math.max(velocityY, - (target as RecyclerView).maxFlingVelocity.toFloat()) val currentOffset = (target as RecyclerView).computeVerticalScrollOffset() if (currentOffset == 0) { super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, false) return true } else { val distance = getFlingDistance(velocityY.toInt()).toFloat() val remainingVelocity = - (distance - currentOffset) * (- velocityY / distance) if (remainingVelocity < 0) { (target as RecyclerView).addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { if (newState == RecyclerView.SCROLL_STATE_IDLE) { recyclerView.post { recyclerView.removeOnScrollListener(this) } if (recyclerView.computeVerticalScrollOffset() == 0) { super@SmoothScrollBehavior.onNestedFling(coordinatorLayout, child, target, velocityX, remainingVelocity, false) } } } }) } return false } } // We won't get here anyway, flings with positive velocity are handled in onNestedPreFling return false } override fun onNestedPreFling(coordinatorLayout: CoordinatorLayout?, child: AppBarLayout, target: View?, velocityX: Float, givenVelocity: Float): Boolean { // Making sure the velocity has the correct sign (seems to be an issue) var velocityY: Float if (isPositive != givenVelocity > 0) { velocityY = givenVelocity * - 1 } else velocityY = givenVelocity if (velocityY > 0) { // Decrement to the maximum velocity if necessary velocityY = Math.min(velocityY, (target as RecyclerView).maxFlingVelocity.toFloat()) val topBottomOffsetForScrollingSibling = AppBarLayout.Behavior::class.java.getDeclaredMethod("getTopBottomOffsetForScrollingSibling").apply { isAccessible = true }.invoke(this) as Int val isCollapsed = topBottomOffsetForScrollingSibling == - child.totalScrollRange // The AppBarlayout is collapsed, we'll let the RecyclerView handle the fling on its own if (isCollapsed) return false // The AppbarLayout is not collapsed, we'll calculate the remaining velocity, trigger the appbar to collapse and fling the RecyclerView manually (if necessary) as soon as that is done val distance = getFlingDistance(velocityY.toInt()) val remainingVelocity = (distance - (child.totalScrollRange + topBottomOffsetForScrollingSibling)) * (velocityY / distance) if (remainingVelocity > 0) { (child as AppBarLayout).addOnOffsetChangedListener(object : AppBarLayout.OnOffsetChangedListener { override fun onOffsetChanged(appBarLayout: AppBarLayout, verticalOffset: Int) { // Collapsed if (verticalOffset == - appBarLayout.totalScrollRange) { (target as RecyclerView).mViewFlinger.fling(velocityX.toInt(), remainingVelocity.toInt()) appBarLayout.post { appBarLayout.removeOnOffsetChangedListener(this) } } } }) } // Trigger the expanding of the AppBarLayout super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, false) // We don't let the RecyclerView fling already return true } else return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY) } override fun onNestedPreScroll(coordinatorLayout: CoordinatorLayout?, child: AppBarLayout?, target: View?, dx: Int, dy: Int, consumed: IntArray?) { super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed) isPositive = dy > 0 } private fun getFlingDistance(velocity: Int): Double { return splineOverScroller::class.java.getDeclaredMethod("getSplineFlingDistance", Int::class.javaPrimitiveType).apply { isAccessible = true }.invoke(splineOverScroller, velocity) as Double } } 

Julian Os是对的。

如果回收者视图低于阈值并滚动,则Manolo Garcia的回答不起作用。 您必须将回收站的offsetvelocity to the distance ,而不是项目位置。

我通过引用朱利安的kotlin代码并减去reflection来制作java版本。

 public final class FlingBehavior extends AppBarLayout.Behavior { private boolean isPositive; private float mFlingFriction = ViewConfiguration.getScrollFriction(); private float DECELERATION_RATE = (float) (Math.log(0.78) / Math.log(0.9)); private final float INFLEXION = 0.35f; private float mPhysicalCoeff; public FlingBehavior(){ init(); } public FlingBehavior(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init(){ final float ppi = BaseApplication.getInstance().getResources().getDisplayMetrics().density * 160.0f; mPhysicalCoeff = SensorManager.GRAVITY_EARTH // g (m/s^2) * 39.37f // inch/meter * ppi * 0.84f; // look and feel tuning } @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) { RecyclerView recyclerView = (RecyclerView) target; double distance = getFlingDistance((int) velocityY); if (distance < recyclerView.computeVerticalScrollOffset()) { consumed = true; } else { consumed = false; } } 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; } public double getFlingDistance(int velocity){ final double l = getSplineDeceleration(velocity); final double decelMinusOne = DECELERATION_RATE - 1.0; return mFlingFriction * mPhysicalCoeff * Math.exp(DECELERATION_RATE / decelMinusOne * l); } private double getSplineDeceleration(int velocity) { return Math.log(INFLEXION * Math.abs(velocity) / (mFlingFriction * mPhysicalCoeff)); } } 

我已经find了由Eniz Bilgin修复https://stackoverflow.com/a/45090239/7639018

这个库已经解决了这个问题。

https://developer.android.com/topic/libraries/support-library/setup.html

 allprojects { repositories { jcenter() maven { url "https://maven.google.com" } } } 

答案:已在支持库v26中修复

但v26有一些问题在扔。 有时候,即使一掷千金,AppBar也会反弹回来。

如何删除appbar上的弹跳效果?

如果在更新到支持v26时遇到同样的问题,这里是这个答案的摘要。

解决scheme :在NestedScroll尚未停止的情况下,触摸AppBar时,扩展AppBar的默认行为并阻止对AppBar.Behavior的onNestedPreScroll()和onNestedScroll()的调用。