android android-recyclerview android-espresso coordinator-layout android-nestedscrollview

android - Espresso, el desplazamiento no funciona cuando NestedScrollView o RecyclerView están en CoordinatorLayout



android-recyclerview android-espresso (7)

Parece que CoordinatorLayout rompe el comportamiento de las acciones de Espresso como scrollTo() o RecyclerViewActions.scrollToPosition() .

Problema con NestedScrollView

Para un diseño como este:

<android.support.design.widget.CoordinatorLayout android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v4.widget.NestedScrollView android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_behavior="@string/appbar_scrolling_view_behavior"> ... </android.support.v4.widget.NestedScrollView> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" > ... </android.support.design.widget.AppBarLayout> </android.support.design.widget.CoordinatorLayout>

Si intento desplazarme a cualquier vista dentro de NestedScrollView usando ViewActions.scrollTo() el primer problema que encuentro es que obtengo una PerformException . Esto se debe a que esta acción solo admite ScrollView y NestedScrollView no la amplía. here se explica una solución para este problema, básicamente podemos copiar el código en scrollTo() y cambiar las restricciones para admitir NestedScrollView . Esto parece funcionar si NestedScrollView no está en un CoordinatorLayout pero tan pronto como lo pones dentro de un CoordinatorLayout la acción de desplazamiento falla.

Problema con RecyclerView

Para el mismo diseño, si sustituyo el NestedScrollView por un RecyclerView también hay problemas con el desplazamiento.

En este caso, estoy usando RecyclerViewAction.scrollToPosition(position) . A diferencia de NestedScrollView , aquí puedo ver algunos eventos de desplazamiento. Sin embargo, parece que se desplaza a la posición incorrecta. Por ejemplo, si me desplazo a la última posición, hace visible la segunda para durar, pero no la última. Cuando muevo el RecyclerView fuera del CoordinatorLayout el desplazamiento funciona como debería.

En este momento no podemos escribir ninguna prueba de Espresso para las pantallas que usan CoordinatorLayout debido a estos problemas. ¿Alguien experimenta los mismos problemas o conoce una solución?


Aquí es cómo hice lo mismo que hizo @miszmaniac en Kotlin. Con la delegación en Kotlin , es mucho más limpio y fácil porque no tengo que anular los métodos que no necesito.

activity.runOnUiThread(new Runnable() { @Override public void run() { // remove CoordinatorLayout.LayoutParams from NestedScrollView NestedScrollView nestedScrollView = (NestedScrollView)activity.findViewById(scrollViewId); CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams)nestedScrollView.getLayoutParams(); params.setBehavior(null); nestedScrollView.requestLayout(); } });


El scrollTo(R.id.button) funciona en todo tipo de vistas desplazables, también en NestedScrollView .

Es útil para solucionar este tipo de problemas con Espresso. Lo desarrollamos y lo usamos solo para escribir las pruebas Espresso de una manera rápida y confiable. Y aquí hay un enlace: https://github.com/SchibstedSpain/Barista


Esto sucede porque el método espresso scrollTo () comprueba explícitamente la clase de diseño y solo funciona para ScrollView y HorizontalScrollView. Internamente, está usando View.requestRectangleOnScreen (...), así que espero que realmente funcione bien para muchos diseños.

Mi solución para NestedScrollView fue tomar ScrollToAction y modificar esa restricción. La acción modificada funcionó bien para NestedScrollView con ese cambio.

Método modificado en la clase ScrollToAction:

public Matcher<View> getConstraints() { return allOf(withEffectiveVisibility(Visibility.VISIBLE), isDescendantOfA(anyOf( isAssignableFrom(ScrollView.class), isAssignableFrom(HorizontalScrollView.class), isAssignableFrom(NestedScrollView.class)))); }

Método de conveniencia:

public static ViewAction betterScrollTo() { return ViewActions.actionWithAssertions(new NestedScrollToAction()); }


He hecho una clase NestedScrollViewScrollToAction.

Creo que es mejor lugar para hacer cosas específicas de la actividad allí.

Lo único que vale la pena mencionar es que el código busca el nestedScrollView principal y elimina el comportamiento de CoordinatorLayout.

https://gist.github.com/miszmaniac/12f720b7e898ece55d2464fe645e1f36


La solución del Sr. Mido puede funcionar en algunas situaciones, pero no siempre. Si tiene alguna vista en la parte inferior de la pantalla, el desplazamiento de su RecyclerView no se realizará porque el clic comenzará fuera de RecyclerView.

Una forma de solucionar este problema es escribir una SwipeAction personalizada. Me gusta esto:

1 - Crear el CenterSwipeAction

public class CenterSwipeAction implements ViewAction { private final Swiper swiper; private final CoordinatesProvider startCoordProvide; private final CoordinatesProvider endCoordProvide; private final PrecisionDescriber precDesc; public CenterSwipeAction(Swiper swiper, CoordinatesProvider startCoordProvide, CoordinatesProvider endCoordProvide, PrecisionDescriber precDesc) { this.swiper = swiper; this.startCoordProvide = startCoordProvide; this.endCoordProvide = endCoordProvide; this.precDesc = precDesc; } @Override public Matcher<View> getConstraints() { return withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE); } @Override public String getDescription() { return "swipe from middle of screen"; } @Override public void perform(UiController uiController, View view) { float[] startCoord = startCoordProvide.calculateCoordinates(view); float[] finalCoord = endCoordProvide.calculateCoordinates(view); float[] precision = precDesc.describePrecision(); // you could try this for several times until Swiper.Status is achieved or try count is reached try { swiper.sendSwipe(uiController, startCoord, finalCoord, precision); } catch (RuntimeException re) { throw new PerformException.Builder() .withActionDescription(this.getDescription()) .withViewDescription(HumanReadables.describe(view)) .withCause(re) .build(); } // ensures that the swipe has been run. uiController.loopMainThreadForAtLeast(ViewConfiguration.getPressedStateDuration()); } }

2 - Crear el método para devolver la ViewAction

private static ViewAction swipeFromCenterToTop() { return new CenterSwipeAction(Swipe.FAST, GeneralLocation.CENTER, view -> { float[] coordinates = GeneralLocation.CENTER.calculateCoordinates(view); coordinates[1] = 0; return coordinates; }, Press.FINGER); }

3 - Luego úsalo para desplazarte por la pantalla:

onView(withId(android.R.id.content)).perform(swipeFromCenterToTop());

¡Y eso es! De esta manera puedes controlar cómo va a pasar el desplazamiento en tu pantalla.


Se ha informado este problema (¿quizás por el OP?), Consulte el Número 203684

Uno de los comentarios a ese problema sugiere una solución al problema cuando NestedScrollView está dentro de un CoordinatorLayout:

debe eliminar el comportamiento de diseño de @string/appbar_scrolling_view_behavior o de cualquier vista principal en la que se incluye @string/appbar_scrolling_view_behavior

Aquí hay una implementación de esa solución:

class ScrollToAction( private val original: android.support.test.espresso.action.ScrollToAction = android.support.test.espresso.action.ScrollToAction() ) : ViewAction by original { override fun getConstraints(): Matcher<View> = anyOf( allOf( withEffectiveVisibility(Visibility.VISIBLE), isDescendantOfA(isAssignableFrom(NestedScrollView::class.java))), original.constraints ) }

Pude hacer que mis pruebas funcionaran mediante:

  1. Realización de una acción scrollTo () personalizada (según lo referenciado por el OP y Turnsole)
  2. Eliminar los parámetros de diseño de NestedScrollView como se muestra aquí

Tuve este problema con CoordinatorLayout-> ViewPager-> NestedScrollView. Me resultó muy fácil solucionar el mismo comportamiento de scrollTo () simplemente deslizar hacia arriba en la pantalla:

onView(withId(android.R.id.content)).perform(ViewActions.swipeUp());