Il semble que CoordinatorLayout
casse le comportement des actions Espresso telles que scrollTo()
ou RecyclerViewActions.scrollToPosition()
.
Pour une mise en page comme celle-ci:
<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 vers n'importe quelle vue à l'intérieur du NestedScrollView
en utilisant ViewActions.scrollTo()
le premier problème que je trouve est que j'obtiens un PerformException
. En effet, cette action prend uniquement en charge ScrollView
et NestedScrollView
ne l'étend pas. Une solution de contournement pour ce problème est expliquée ici , fondamentalement, nous pouvons copier le code dans scrollTo()
et changer les contraintes pour prendre en charge NestedScrollView
. Cela semble fonctionner si le NestedScrollView
n'est pas dans un CoordinatorLayout
mais dès que vous le placez dans un CoordinatorLayout
l'action de défilement échoue.
Pour la même mise en page, si je remplace le NestedScrollView
par un RecyclerView
il y a aussi des problèmes de défilement.
Dans ce cas, j'utilise RecyclerViewAction.scrollToPosition(position)
. Contrairement au NestedScrollView
, ici je peux voir un certain défilement. Cependant, il semble qu'il défile dans la mauvaise position. Par exemple, si je défile jusqu'à la dernière position, cela rend visible l'avant-dernier mais pas le dernier. Lorsque je déplace le RecyclerView
hors du 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 ces problèmes. Quelqu'un connaît les mêmes problèmes ou connaît une solution?
Cela se produit car la méthode Espresso scrollTo () vérifie explicitement la classe de disposition et ne fonctionne que pour ScrollView et HorizontalScrollView. En interne, il utilise View.requestRectangleOnScreen (...), je m'attends donc à ce que cela fonctionne réellement pour de nombreuses mises en page.
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.
Méthode modifiée dans la classe ScrollToAction:
public Matcher<View> getConstraints() {
return allOf(withEffectiveVisibility(Visibility.VISIBLE), isDescendantOfA(anyOf(
isAssignableFrom(ScrollView.class), isAssignableFrom(HorizontalScrollView.class), isAssignableFrom(NestedScrollView.class))));
}
Méthode pratique:
public static ViewAction betterScrollTo() {
return ViewActions.actionWithAssertions(new NestedScrollToAction());
}
Voici comment j'ai fait la même chose que @miszmaniac à Kotlin. Avec délégation dans Kotlin , c'est beaucoup plus propre et plus facile car je n'ai pas à remplacer 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 eu ce problème avec CoordinatorLayout-> ViewPager-> NestedScrollView un travail facile autour de moi pour obtenir le même comportement scrollTo () consistait à simplement balayer l'écran:
onView(withId(Android.R.id.content)).perform(ViewActions.swipeUp());
Ce problème a été signalé (peut-être par l'OP?), Voir problème 203684
L'un des commentaires sur ce problème suggère une solution de contournement au problème lorsque le NestedScrollView est à l'intérieur d'un CoordinatorLayout:
vous devez supprimer le
@string/appbar_scrolling_view_behavior
comportement de mise en page du ScrollingView ou de toute vue parente dans laquelle ce ScrollingView est inclus
Voici une implémentation de cette solution de contournement:
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();
}
});
J'ai pu faire fonctionner mes tests par:
La scrollTo(R.id.button)
de Barista fonctionne sur toutes sortes de vues déroulantes, également sur NestedScrollView
.
Il est utile de résoudre ce type de problèmes avec Espresso. Nous le développons et l'utilisons juste pour écrire des tests Espresso de manière rapide et fiable. Et voici un lien: https://github.com/SchibstedSpain/Barista
La solution de M. Mido peut fonctionner dans certaines situations, mais pas toujours. Si vous avez une vue en bas de l'écran, le défilement de votre RecyclerView ne se produira pas car le clic commencera en dehors de RecyclerView.
Une façon de contourner ce problème consiste à écrire un SwipeAction personnalisé. Comme ça:
1 - Créez 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éez 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 - Ensuite, utilisez-le pour faire défiler l'écran:
onView(withId(Android.R.id.content)).perform(swipeFromCenterToTop());
Et c'est tout! De cette façon, vous pouvez contrôler la façon dont le défilement se produira sur votre écran.
J'ai créé une classe NestedScrollViewScrollToAction.
Je pense que c'est un meilleur endroit pour y faire des choses spécifiques à l'activité.
La seule chose à noter est que le code recherche le parent nestedScrollView et supprime son comportement CoordinatorLayout.
https://Gist.github.com/miszmaniac/12f720b7e898ece55d2464fe645e1f36