android - tutorial - Seleccionando la vista del niño en el índice usando Espresso
test ui android (3)
Para intentar mejorar un poco la solución de Maragues, he hecho algunos cambios.
La solución es crear un <View> Matcher personalizado que envuelva otro <View> Matcher para la vista principal, y tome el índice de la vista secundaria para que coincida.
public static Matcher<View> nthChildOf(final Matcher<View> parentMatcher, final int childPosition) {
return new TypeSafeMatcher<View>() {
@Override
public void describeTo(Description description) {
description.appendText("position " + childPosition + " of parent ");
parentMatcher.describeTo(description);
}
@Override
public boolean matchesSafely(View view) {
if (!(view.getParent() instanceof ViewGroup)) return false;
ViewGroup parent = (ViewGroup) view.getParent();
return parentMatcher.matches(parent)
&& parent.getChildCount() > childPosition
&& parent.getChildAt(childPosition).equals(view);
}
};
}
Explicación detallada
Puede anular el método describeTo para dar una descripción fácil de entender del emparejador agregando el argumento Descripción . También querrá propagar la llamada describeTo al emparejador principal para que su descripción también se agregue.
@Override
public void describeTo(Description description) {
description.appendText("position " + childPosition + " of parent "); // Add this matcher''s description.
parentMatcher.describeTo(description); // Add the parentMatcher description.
}
A continuación, debe anular las coincidencias Seguramente, lo que determinará cuándo se ha encontrado una coincidencia en la jerarquía de vistas. Cuando se le llama con una vista cuyo padre coincide con el comparador principal proporcionado, verifique que la vista sea igual a la secundaria en la posición proporcionada.
Si el padre no tiene un childCount mayor que la posición secundaria, getChildAt devolverá el valor nulo y hará que la prueba se bloquee. Es mejor evitar los choques y permitir que la prueba falle para que recibamos un informe de prueba y un mensaje de error adecuados.
@Override
public boolean matchesSafely(View view) {
if (!(view.getParent() instanceof ViewGroup)) return false; // If it''s not a ViewGroup we know it doesn''t match.
ViewGroup parent = (ViewGroup) view.getParent();
return parentMatcher.matches(parent) // Check that the parent matches.
&& parent.getChildCount() > childPosition // Make sure there''s enough children.
&& parent.getChildAt(childPosition).equals(view); // Check that this is the right child.
}
Con Espresso cuando se usa una vista de widget personalizada con vistas de imágenes secundarias, ¿qué tipo de Matcher puedo usar para seleccionar el n ° hijo? Ejemplo:
+--------->NumberSlider{id=2131296844, res-name=number_slider, visibility=VISIBLE, width=700, height=95, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=10.0, y=0.0, child-count=7}
|
+---------->NumberView{id=-1, visibility=VISIBLE, width=99, height=95, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=true, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0}
|
+---------->NumberView{id=-1, visibility=VISIBLE, width=100, height=95, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=true, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=99.0, y=0.0}
|
+---------->NumberView{id=-1, visibility=VISIBLE, width=100, height=95, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=true, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=199.0, y=0.0}
|
+---------->NumberView{id=-1, visibility=VISIBLE, width=100, height=95, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=true, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=299.0, y=0.0}
|
+---------->NumberView{id=-1, visibility=VISIBLE, width=100, height=95, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=true, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=399.0, y=0.0}
|
+---------->NumberView{id=-1, visibility=VISIBLE, width=100, height=95, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=true, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=499.0, y=0.0}
|
+---------->NumberView{id=-1, visibility=VISIBLE, width=100, height=95, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=true, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=599.0, y=0.0}
Si puede obtener la vista principal. Puede ser este enlace que definió un emparejador para obtener el primer elemento secundario de una vista que puede darle alguna pista.
public static Matcher<View> firstChildOf(final Matcher<View> parentMatcher) {
return new TypeSafeMatcher<View>() {
@Override
public void describeTo(Description description) {
description.appendText("with first child view of type parentMatcher");
}
@Override
public boolean matchesSafely(View view) {
if (!(view.getParent() instanceof ViewGroup)) {
return parentMatcher.matches(view.getParent());
}
ViewGroup group = (ViewGroup) view.getParent();
return parentMatcher.matches(view.getParent()) && group.getChildAt(0).equals(view);
}
};
}
public static Matcher<View> nthChildOf(final Matcher<View> parentMatcher, final int childPosition) {
return new TypeSafeMatcher<View>() {
@Override
public void describeTo(Description description) {
description.appendText("with "+childPosition+" child view of type parentMatcher");
}
@Override
public boolean matchesSafely(View view) {
if (!(view.getParent() instanceof ViewGroup)) {
return parentMatcher.matches(view.getParent());
}
ViewGroup group = (ViewGroup) view.getParent();
return parentMatcher.matches(view.getParent()) && group.getChildAt(childPosition).equals(view);
}
};
}
Para usarlo
onView(nthChildOf(withId(R.id.parent_container), 0)).check(matches(withText("I am the first child")));