Lancer avec RecyclerView + AppBarLayout

J’utilise le nouveau CoordinatorLayout avec AppBarLayout et CollapsingToolbarLayout. Sous AppBarLayout, j’ai un RecyclerView avec une liste de contenu.

J’ai vérifié que le défilement automatique fonctionne sur RecyclerView lorsque je fais défiler la liste vers le haut ou le bas. Cependant, je voudrais aussi que AppBarLayout défile en douceur lors de l’extension.

Lorsque vous faites défiler l’écran pour développer CollaspingToolbarLayout, le défilement cesse immédiatement une fois que vous relâchez l’écran. Si vous faites défiler rapidement, parfois, CollapsingToolbarLayout se replie également. Ce comportement avec RecyclerView semble fonctionner différemment que lors de l’utilisation d’un NestedScrollView.

J’ai essayé de définir différentes propriétés de défilement sur la liste de recyclage, mais je n’ai pas réussi à le comprendre.

Voici une vidéo montrant certains des problèmes de défilement. https://youtu.be/xMLKoJOsTAM

Voici un exemple montrant le problème avec RecyclerView (CheeseDetailActivity). https://github.com/tylerjroach/cheesesquare

Voici l’exemple original qui utilise un NestedScrollView de Chris Banes. https://github.com/chrisbanes/cheesesquare

La réponse de Kirill Boyarshinov était presque correcte.

Le principal problème est que RecyclerView donne parfois une direction incorrecte, donc si vous ajoutez le code suivant à sa réponse, cela fonctionne correctement:

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

J’espère que ca aide.

Semble que la mise à jour v23 n’a pas encore v23 problème.

J’ai trouvé une sorte de piratage pour résoudre le problème en le lançant. L’astuce consiste à reconsidérer l’événement de fling si l’enfant principal de ScrollingView est proche du début des données dans Adapter.

 public final class FlingBehavior extends AppBarLayout.Behavior { public FlingBehavior() { } public FlingBehavior(Context context, AtsortingbuteSet 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); } } 

Utilisez-le dans votre mise en page comme ça:

     

EDIT: L’ événement Fling reconsuming est maintenant basé sur verticalScrollOffset au lieu de la quantité d’éléments en haut de RecyclerView .

EDIT2: Vérifiez la cible en tant ScrollingView interface ScrollingView au lieu de RecyclerView . RecyclerView et NestedScrollingView implémentent tous les deux.

J’ai trouvé le correctif en appliquant OnScrollingListener au recyclerView. maintenant ça marche très bien. Le problème est que recyclerview fourni la mauvaise valeur consommée et le comportement ne sait pas quand la recyclerview est fait défiler vers le haut.

 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.AtsortingbuteSet; 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 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 coordinatorLayoutRef; private WeakReference childRef; private WeakReference behaviorWeakReference; public RecyclerViewScrollListener(CoordinatorLayout coordinatorLayout, AppBarLayout child, RecyclerViewAppBarBehavior barBehavior) { coordinatorLayoutRef = new WeakReference(coordinatorLayout); childRef = new WeakReference(child); behaviorWeakReference = new WeakReference(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); } } } } 

Il a été corrigé depuis la conception du support 26.0.0.

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

Ceci est une version fluide de Google Support Design AppBarLayout. Si vous utilisez AppBarLayout, vous saurez qu’il y a un problème avec fling.

 comstack "me.henrytao:smooth-app-bar-layout:" 

Voir la bibliothèque ici .. https://github.com/henrytao-me/smooth-app-bar-layout

C’est un bug de recyclerview. Il est supposé être corrigé dans la v23.1.0.

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

entrer la description de l'image ici

Ceci est ma mise en page et le défilement Cela fonctionne comme il se doit.

         

Ma solution jusqu’ici, basée sur Mak Sing et Manolo Garcia, répond.

Ce n’est pas totalement parfait. Pour l’instant, je ne sais pas comment recalculer une vitesse valide pour éviter un effet étrange: la barre d’application peut s’étendre plus rapidement que la vitesse de défilement. Mais l’état avec une barre d’application étendue et une vue de recyclage défilée ne peut pas être atteint.

 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.AtsortingbuteSet; 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, AtsortingbuteSet 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 mAppBarLayoutWeakReference; @NonNull private final WeakReference mBehaviorWeakReference; @NonNull private final WeakReference 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; } } } 

Dans mon cas, je commençais à avoir le problème de ne pas faire défiler le RecyclerView manière fluide, le bloquant.

C’était parce que, pour une raison quelconque, j’avais oublié que j’avais mis mon RecyclerView dans un NestedScrollView .

C’est une erreur stupide, mais il m’a fallu du temps pour le comprendre …

J’ajoute une vue de 1dp de hauteur à l’intérieur de l’AppBarLayout et ça marche beaucoup mieux. Ceci est ma mise en page

        

Déjà des solutions très populaires ici, mais après avoir joué avec elles, j’ai trouvé une solution plutôt simple qui a bien fonctionné pour moi. Ma solution garantit également que AppBarLayout n’est étendu que lorsque le contenu défilant atteint le sumt, un avantage par rapport aux autres solutions.

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

La réponse acceptée ne fonctionnait pas pour moi car j’avais RecyclerView dans SwipeRefreshLayout et un ViewPager . Ceci est la version améliorée qui cherche un RecyclerView dans la hiérarchie et devrait fonctionner pour n’importe quelle mise en page:

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

Réponse: il est corrigé dans la bibliothèque de support v26

mais v26 a un problème dans le lancer. Parfois, AppBar rebondit même si le lancer n’est pas trop difficile.

Comment supprimer l’effet de rebond sur Appbar?

Si vous rencontrez le même problème lors de la mise à jour pour prendre en charge la version 26, voici le résumé de cette réponse .

Solution : Étendez le comportement par défaut d’AppBar et bloquez l’appel des onNestedPreScroll () et onNestedScroll () AppBar.Behavior lorsque AppBar est touché alors que NestedScroll ne s’est pas encore arrêté.

Julian Os a raison.

La réponse de Manolo Garcia ne fonctionne pas si la liste de recyclage est en dessous du seuil et défile. Vous devez comparer le offset de la recyclerview et la velocity to the distance , pas la position de l’élément.

J’ai fait la version java en me référant au code kotlin de julian et en soustrayant la reflection.

 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, AtsortingbuteSet attrs) { super(context, attrs); init(); } private void init(){ final float ppi = BaseApplication.getInstance().getResources().getDisplayMesortingcs().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)); } } 

J’ai trouvé le correctif par Eniz Bilgin https://stackoverflow.com/a/45090239/7639018

Le problème a été résolu avec les bibliothèques de ce référentiel.

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

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

En ce qui concerne le suivi des problèmes Google , il a été corrigé avec la version Android 26.0.0-beta2 de la bibliothèque de support.

Veuillez mettre à jour la version 26.0.0-beta2 de votre bibliothèque de support Android.

Si un problème persiste, veuillez vous reporter à l’ outil de suivi des problèmes Google qu’ils ré-ouvriront pour examiner.

Ajouter une autre réponse ici, car les réponses ci-dessus ne répondaient pas complètement à mes besoins ou ne fonctionnaient pas très bien. Celui-ci est partiellement basé sur des idées répandues ici.

Alors qu’est-ce que celui-ci fait?

Scénario vers le bas: Si AppBarLayout est réduit, il permet au RecyclerView de se lancer sans rien faire. Sinon, il réduit AppBarLayout et empêche RecyclerView de faire son envoi. Dès qu’il est réduit (jusqu’à ce que la vitesse demandée l’exige) et qu’il rest de la vitesse, le RecyclerView est lancé avec la vitesse d’origine moins ce que l’AppBarLayout vient de consumr.

Scénario vers le haut: Si le décalage de défilement de RecyclerView n’est pas nul, il est lancé avec la vitesse d’origine. Dès que cela est terminé et s’il rest de la vélocité (c.-à-d. Que RecyclerView a défilé à la position 0), l’AppBarLayout est développé jusqu’au point où la vélocité d’origine moins les demandes qui viennent d’être consommées. Sinon, AppBarLayout est développé au point que la vélocité d’origine l’exige.

AFAIK, c’est le comportement prévu.

Il y a beaucoup de reflection, et c’est assez coutume. Aucun problème n’a encore été trouvé. Il est également écrit en Kotlin, mais comprendre cela ne devrait poser aucun problème. Vous pouvez utiliser le plug-in IntelliJ Kotlin pour le comstackr en bytecode -> et le décomstackr en Java. Pour l’utiliser, placez-le dans le package android.support.v7.widget et définissez-le comme le comportement CoordinatorLayout.LayoutParams de AppBarLayout dans le code (ou ajoutez le constructeur applicable à xml ou quelque chose)

 /* * 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 * dissortingbuted under the License is dissortingbuted 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're not getting 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, sortinggger 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) { // The AppBarLayout is now collapsed if (verticalOffset == - appBarLayout.totalScrollRange) { (target as RecyclerView).mViewFlinger.fling(velocityX.toInt(), remainingVelocity.toInt()) appBarLayout.post { appBarLayout.removeOnOffsetChangedListener(this) } } } }) } // Trigger the expansion 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 } } 

c’est ma solution dans mon projet.
arrêtez simplement le mScroller quand obtenir Action_Down

xml:

   

FixAppBarLayoutBehavior.java:

  public boolean onInterceptTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent ev) { if (ev.getAction() == ACTION_DOWN) { Object scroller = getSuperSuperField(this, "mScroller"); if (scroller != null && scroller instanceof OverScroller) { OverScroller overScroller = (OverScroller) scroller; overScroller.abortAnimation(); } } return super.onInterceptTouchEvent(parent, child, ev); } private Object getSuperSuperField(Object paramClass, Ssortingng paramSsortingng) { Field field = null; Object object = null; try { field = paramClass.getClass().getSuperclass().getSuperclass().getDeclaredField(paramSsortingng); field.setAccessible(true); object = field.get(paramClass); } catch (Exception e) { e.printStackTrace(); } return object; } //or check the raw file: //https://github.com/shaopx/CoordinatorLayoutExample/blob/master/app/src/main/java/com/spx/coordinatorlayoutexample/util/FixAppBarLayoutBehavior.java