Espresso, scrolling ne fonctionne pas lorsque NestedScrollView ou RecyclerView est en CoordinatorLayout
Il ressemble à CoordinatorLayout
brise le comportement des actions Espresso telles que scrollTo()
ou RecyclerViewActions.scrollToPosition()
.
Problème avec NestedScrollView
Pour une mise en page comme ceci:
<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 j'essaie de faire défiler à tout point de vue à l'intérieur de l' NestedScrollView
en utilisant ViewActions.scrollTo()
le premier problème que j'ai trouver c'est que je reçois un PerformException
. C'est parce que cette action ne supporte que ScrollView
et NestedScrollView
ne l'étend pas. Une solution pour contourner ce problème est expliquée ici, fondamentalement, nous pouvons copier le code dans scrollTo()
et changez la contrainte de support NestedScrollView
. Cela semble fonctionner si le NestedScrollView
n'est pas dans un CoordinatorLayout
mais dès que vous le mettez à l'intérieur d'un CoordinatorLayout
l'action de défilement échoue.
problème avec RecyclerView
Pour la même mise en page, si je remplace le NestedScrollView
avec un RecyclerView
il y a aussi des problèmes avec le défilement.
dans ce cas, j'utilise RecyclerViewAction.scrollToPosition(position)
. Contrairement à l' NestedScrollView
, ici, je peux voir quelques défilement qui se passe. Cependant, il semble qu'il défile à la mauvaise position. Par exemple, si je fais défiler jusqu'à la dernière position, cela rend visible l'avant-dernier mais pas le dernier. Quand je bouge l' RecyclerView
de la CoordinatorLayout
le défilement fonctionne comme il se doit.
pour le moment nous ne pouvons pas écrire de test Espresso pour les écrans qui utilisent CoordinatorLayout
en raison de ce problème. Quelqu'un rencontre le même problème ou connait une solution?
7 réponses
cela se produit parce que la méthode Espresso scrollTo() vérifie explicitement la classe layout et ne fonctionne que pour ScrollView & HorizontalScrollView. En interne, c'est à l'aide de la Vue.requestRectangleOnScreen(...) donc je m'attends vraiment beau travail pour de nombreux modèles.
ma solution de contournement pour NestedScrollView était de prendre ScrollToAction et de modifier cette contrainte. L'action modifiée a bien fonctionné pour NestedScrollView avec ce changement.
public Matcher<View> getConstraints() {
return allOf(withEffectiveVisibility(Visibility.VISIBLE), isDescendantOfA(anyOf(
isAssignableFrom(ScrollView.class), isAssignableFrom(HorizontalScrollView.class), isAssignableFrom(NestedScrollView.class))));
}
méthode de commodité:
public static ViewAction betterScrollTo() {
return ViewActions.actionWithAssertions(new NestedScrollToAction());
}
j'ai eu ce problème avec CoordinatorLayout->ViewPager->NestedScrollView un travail facile, autour de moi, pour obtenir le même scrollTo() le comportement a été à juste balayez vers le haut sur l'écran:
onView(withId(android.R.id.content)).perform(ViewActions.swipeUp());
Ce problème a été signalé (peut-être par l'OP?), voir numéro 203684
L'un des commentaires à ce sujet suggère une solution de rechange au problème lorsque le lecteur NestedScrollView est à l'intérieur D'un tableau de coordination:
vous devez supprimer le
@string/appbar_scrolling_view_behavior
comportement de mise en page de la vue ScrollingView ou d'une vue parent cette vue ScrollingView est incluse dans
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();
}
});
je j'ai réussi à faire fonctionner mes tests en:
- Faire un personnalisé scrollTo() action (tel que référencé par les OP et Turnsole)
- suppression des paramètres de mise en page de NestedScrollView comme indiqué ici
Voici comment j'ai fait la même chose que @miszmaniac à Kotlin. délégation de Kotlin, c'est beaucoup plus propre et plus facile parce que je n'ai pas à outrepasser les méthodes dont je n'ai pas besoin.
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
)
}
j'ai fait une classe NestedScrollViewScrollToAction.
je pense que c'est un meilleur endroit pour y faire des activités spécifiques.
la seule chose à mentionner est que le code cherche pour le parent nestedScrollView et supprime son comportement de CoordinatorLayout.
https://gist.github.com/miszmaniac/12f720b7e898ece55d2464fe645e1f36
LA solution DE M. Mido peut fonctionner dans certaines situations, mais pas toujours. Si vous avez une vue en bas de l'écran, le scroll de votre RecyclerView ne se produira pas parce que le clic commencera à l'extérieur de la RecyclerView.
Une façon de contourner ce problème est d'écrire une coutume SwipeAction. Comme ceci:
1 - Créer le 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 - Créer la méthode pour retourner le 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 - Puis l'utiliser pour faites défiler l'écran:
onView(withId(android.R.id.content)).perform(swipeFromCenterToTop());
Et c'est tout! De cette façon, vous pouvez contrôler le défilement est va arriver dans votre écran.
Barista est scrollTo(R.id.button)
fonctionne sur toutes sortes de vues scrollables, aussi sur NestedScrollView
.
il est utile de régler ce genre de problèmes avec L'Espresso. Nous le développons et l'utilisons juste pour écrire des tests Espresso d'une manière rapide et fiable. Et voici un lien: https://github.com/SchibstedSpain/Barista