Перемещение с помощью RecyclerView + AppBarLayout

Я использую новый CoordinatorLayout с AppBarLayout и CollapsingToolbarLayout. Ниже AppBarLayout у меня есть RecyclerView со списком контента.

Я проверил, что прокрутка прокрутки работает в RecyclerView, когда я прокручиваю вверх и вниз по списку. Тем не менее, мне также хотелось бы, чтобы AppBarLayout плавно прокручивал во время расширения.

При прокрутке вверх, чтобы развернуть CollaspingToolbarLayout, прокрутка сразу останавливается, когда вы поднимаете палец с экрана. Если вы прокручиваете вверх по быстрому движению, иногда CollapsingToolbarLayout снова свертывается. Такое поведение с RecyclerView, похоже, работает по-разному, чем при использовании NestedScrollView.

Я попытался установить различные свойства прокрутки в recyclerview, но я не смог понять это.

Вот видео, показывающее некоторые проблемы с прокруткой. https://youtu.be/xMLKoJOsTAM

Вот пример, показывающий проблему с RecyclerView (CheeseDetailActivity). https://github.com/tylerjroach/cheesesquare

Вот оригинальный пример, который использует NestedScrollView от Chris Banes. https://github.com/chrisbanes/cheesesquare

Ответ Кирилла Бояршинова был почти правильным.

Основная проблема заключается в том, что 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 еще не исправило.

Я нашел вид взлома, чтобы исправить это. Хитрость состоит в том, чтобы пересмотреть событие fling, если верхний дочерний элемент ScrollingView близок к началу данных в Adapter.

 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> 

EDIT: Регенерация событий Fling теперь основана на verticalScrollOffset вместо количества элементов сверху с RecyclerView .

EDIT2: Проверьте цель как экземпляр интерфейса ScrollingView вместо RecyclerView . И RecyclerView и NestedScrollingView реализуют его.

Я нашел исправление, применив OnScrollingListener к recyclerView. Теперь он работает очень хорошо. Проблема в том, что recyclerview предоставил неправильную потребляемую ценность, и поведение не знает, когда 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); } } } } 

Это плавная версия AppBarLayout для поддержки Google. Если вы используете AppBarLayout, вы узнаете, что у него есть проблема с броском.

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

См. Здесь библиотеку. https://github.com/henrytao-me/smooth-app-bar-layout

Он был исправлен с момента разработки 26.0.0.

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

Это ошибка recyclerview. Он должен быть исправлен в 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> 

Мое решение до сих пор основано на Мак Синге и Маноло Гарсиа .

Это не совсем прекрасно. Пока я не знаю, как пересчитать скорость валида, чтобы избежать странного эффекта: панель приложения может расширяться быстрее, чем скорость прокрутки. Но состояние с расширенной панелью объявлений и прокрученным просмотром ресайклера не может быть достигнуто.

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

Я добавляю представление высоты 1dp внутри AppBarLayout, тогда он работает намного лучше. Это мой макет.

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

Это глупая ошибка, но мне потребовалось некоторое время, чтобы понять это …

Уже некоторые довольно популярные решения здесь, но после игры с ними я придумал довольно простое решение, которое хорошо сработало для меня. Мое решение также гарантирует, что AppBarLayout будет расширяться только тогда, когда прокручиваемый контент достигнет вершины, преимущество над другими решениями здесь.

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

Добавление другого ответа здесь, поскольку вышеупомянутые либо полностью не удовлетворяли моим потребностям, либо не работали очень хорошо. Это частично основано на распространенных здесь идеях.

Так что же это делает?

Сценарий вниз fling: Если AppBarLayout рухнул, он позволяет RecyclerView самостоятельно, без каких-либо действий. В противном случае он разрушает AppBarLayout и не позволяет RecyclerView выполнять его сброс. Как только он рухнет (до такой степени, что заданная скорость требует), и если есть скорость, RecyclerView будет сброшен с исходной скоростью минус то, что только что поглотило AppBarLayout.

Сценарий вверх: если смещение прокрутки RecyclerView не равно нулю, оно обрывается исходной скоростью. Как только это будет закончено, и если все еще есть скорость (т. Е. RecyclerView прокручивается до позиции 0), AppBarLayout расширяется до такой степени, что первоначальная скорость минус только что потребляемая потребляет. В противном случае AppBarLayout расширяется до такой степени, что требует первоначальная скорость.

AFAIK, это исправленное поведение.

Существует много размышлений, и это довольно обычай. Однако проблем не обнаружено. Это также написано в Котлине, но понимание этого не должно быть проблемой. Вы можете использовать плагин IntelliJ Kotlin для компиляции его в байт-код -> и декомпилировать его обратно на Java. Чтобы использовать его, поместите его в пакет android.support.v7.widget и установите его как поведение CoordinatorLayout.LayoutParams в AppBarLayout в коде (или добавьте подходящий конструктор 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 } } 

Принятый ответ не работал для меня, потому что у меня был RecyclerView внутри SwipeRefreshLayout и ViewPager . Это улучшенная версия, которая ищет 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; } } 

Джулиан Ос прав.

Ответ Маноло Гарсиа не работает, если recyclerview ниже порога и свитков. Вы должны сравнить offset recyclerview и velocity to the distance , а не позицию позиции.

Я сделал java-версию, обратившись к котиновскому коду julian и вычитаю отражение.

 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)); } } 
Intereting Posts
Почему setGravity () не влияет на пользовательскую ViewGroup? Как найти планшет или телефон в Android, программно? Crashlytics Gradle Plugin – дополнительные свойства не изменяются Как синхронизировать контакты телефона в gmail в android программно с помощью google-контактов api Не удается запустить AVD (эмулятор) Webview не показывает гиперссылки Как интегрировать два или более файла google-services.json вместе для разных сервисов google в одном проекте с помощью Android Studio Установка нового поставщика местоположений Как я могу запутать свой код в Eclipse? (Android) Можем ли мы установить INSTALL_REFERRER два раза в одном приложении для Android? API WooCommerce для мобильного приложения RingtonePreference не срабатывает OnPreferenceChangeListener Проверка в EditText позволяет использовать IP-адрес или веб-узел Url Android. Как работает метод notifyDataSetChanged () и ListViews? Как установить флажок Android по умолчанию для андроида с предупреждением?