Detener AppBarLayout desplazarse fuera de la pantalla cuando NestedScrollView está vacío

Tengo una característica bastante típica de la lista usando un CoordinatorLayout, AppBarLayout, SwipeRefreshLayout y RecyclerView –

Cuando el RecyclerView tiene suficiente contenido para desplazarse, la página parece estar bien. Cuando el RecyclerView está vacío o no tiene suficiente contenido para desplazarse, sin embargo, el comportamiento es que los niños de AppBarLayout con app:layout_scrollFlags="scroll|enterAlwaysCollapsed" continuarán app:layout_scrollFlags="scroll|enterAlwaysCollapsed" – lo que parece extraño.

¿Hay una manera de detener el desplazamiento de los niños de AppBarLayout cuando el NestedScrollView está vacío?

Introduzca aquí la descripción de la imagen

 <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.design.widget.CoordinatorLayout android:id="@+id/coordinatorLayout" android:background="@android:color/transparent" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/transparent" android:elevation="4dp"> <LinearLayout android:id="@+id/eventHeader" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="16dp" android:background="@color/green" android:orientation="horizontal" app:layout_scrollFlags="scroll|enterAlwaysCollapsed"> <View android:layout_width="0dp" android:layout_height="0dp" android:layout_weight="1"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="scroll|enterAlwaysCollapsed" android:textColor="@color/white" android:textSize="15sp"/> <View android:layout_width="0dp" android:layout_height="0dp" android:layout_weight="1"/> </LinearLayout> </android.support.design.widget.AppBarLayout> <android.support.v4.widget.SwipeRefreshLayout android:id="@+id/swipeToRefresh" android:layout_width="match_parent" android:layout_gravity="fill_vertical" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <android.support.v7.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/transparent" android:dividerHeight="0dp" android:layout_gravity="fill_vertical" android:drawSelectorOnTop="true" android:listSelector="@drawable/selector_ripple_grey_transparent" android:scrollbars="vertical"/> </android.support.v4.widget.SwipeRefreshLayout> </android.support.design.widget.CoordinatorLayout> <TextView android:id="@+id/noData" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:padding="16dp" android:text="@string/no_data_available" android:textSize="17sp"/> </FrameLayout> 

No estoy seguro de lo elegante que es una solución, pero, he sobrepasado el evento onStartNestedScroll() para disparar sólo si el NestedScrollView es desplazable (en este caso, un RecyclerView)

En onCreate ():

 CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) mAppBarLayout.getLayoutParams(); layoutParams.setBehavior(new AppBarLayoutNoEmptyScrollBehavior(mAppBarLayout, mRecyclerView)); 

Comportamiento:

 public class AppBarLayoutNoEmptyScrollBehavior extends AppBarLayout.Behavior { AppBarLayout mAppBarLayout; RecyclerView mRecyclerView; public AppBarLayoutNoEmptyScrollBehavior(AppBarLayout appBarLayout, RecyclerView recyclerView) { mAppBarLayout = appBarLayout; mRecyclerView = recyclerView; } public boolean isRecylerViewScrollable(RecyclerView recyclerView) { int recyclerViewHeight = recyclerView.getHeight(); // Height includes RecyclerView plus AppBarLayout at same level int appCompatHeight = mAppBarLayout != null ? mAppBarLayout.getHeight() : 0; recyclerViewHeight -= appCompatHeight; return recyclerView.computeVerticalScrollRange() > recyclerViewHeight; } @Override public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child, View directTargetChild, View target, int nestedScrollAxes) { if (isRecylerViewScrollable(mRecyclerView)) { return super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes); } return false; } } 

EDITAR

Solución editada como RecyclerView da altura como altura visible de RecyclerView y altura de AppBarLayout (que es la altura de CoordinatorLayout).

Sin embargo, si su gesto de desplazamiento comienza en el área visible AppBarLayout, un desplazamiento todavía se llevará a cabo, incluso si agrega este comportamiento al AppBarLayout también. Esta respuesta por lo tanto no es una solución para el problema.

(Basado en: Referencia )

(1) Cree esta clase.

 public class AppBarLayoutBehaviorForEmptyRecyclerView extends AppBarLayout.Behavior { private boolean canRecyclerViewBeScrolled = false; public AppBarLayoutBehaviorForEmptyRecyclerView() { } public AppBarLayoutBehaviorForEmptyRecyclerView(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean onInterceptTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent ev) { return canRecyclerViewBeScrolled && super.onInterceptTouchEvent(parent, child, ev); } @Override public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child, View directTargetChild, View target, int nestedScrollAxes) { updateScrollable(target); return canRecyclerViewBeScrolled && super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes); } @Override public boolean onNestedFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY, boolean consumed) { return canRecyclerViewBeScrolled && super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed); } private void updateScrollable(View targetChild) { if(targetChild instanceof RecyclerView) { RecyclerView.Adapter adapter = ((RecyclerView) targetChild).getAdapter(); canRecyclerViewBeScrolled = adapter != null && adapter.getItemCount() > 0; } else { canRecyclerViewBeScrolled = true; } } } 

(2) Agregue a su elemento XML AppBarLayout el siguiente atributo:

 app:layout_behavior="com.xxxx.xxxxxx.AppBarLayoutBehaviorForEmptyRecyclerView" 

Después de cargar los datos en su RecyclerView, si está vacía, simplemente despliegue la bandera de desplazamiento manualmente:

 AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) mEventHeader.getLayoutParams(); toolbarLayoutParams.setScrollFlags(0); mEventHeader.setLayoutParams(toolbarLayoutParams); 

Y si no está vacío, retroceda la bandera de desplazamiento:

 AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) mEventHeader.getLayoutParams(); toolbarLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS); mEventHeader.setLayoutParams(toolbarLayoutParams); 

Graeme respuesta está bien, pero también se agregó en el constructor

 public AppBarLayoutOnEmptyRecyclerViewScrollBehavior(@NonNull AppBarLayout appBarLayout, @NonNull RecyclerView recyclerView) { this.appBarLayout = checkNotNull(appBarLayout); this.recyclerView = checkNotNull(recyclerView); setDragCallback(new DragCallback() { @Override public boolean canDrag(@NonNull AppBarLayout appBarLayout) { return isRecylerViewScrollable(recyclerView); } }); } 

Así que cuando RecyclerView está vacío también deshabilito el arrastre de AppBarLayout

  • Barra de herramientas desplazándose bajo la barra de estado
  • AppBarLayout extrañamente toma espacio y empuja el contenido hacia abajo
  • AppBarLayout.onCreateDrawableState NullPointerException: Intento de obtener longitud de matriz nula
  • Android AppBarLayout superpone la vista de lista
  • CoordinatorLayout + AppbarLayout + Viewpager no cambia el tamaño del diseño secundario
  • Fling suavemente AppBarLayout con NestedScrollView usando AppBarLayout.Behavior
  • Cómo deshabilitar el desplazamiento de AppBarLayout en CoordinatorLayout?
  • ¿Cómo funciona la matemática de AppBarLayout.ScrollingViewBehavior?
  • Desactivar el desplazamiento de la barra de herramientas
  • Vista fija en el diseño de contenido de Viewpager, bajo CoordinatorLayout
  • ¿Ocultar barra de herramientas en actividad desplazando una vista de reciclaje dentro del fragmento?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.