en Espresso, cómo elegir uno que tenga la misma identificación para evitar AmbiguousViewMatcherException

Tener gridView que tiene algunas imágenes. La celda de gridView sale del mismo diseño predefinido, que tiene el mismo ID y desc.

R.id.item_image == 2131493330

onView(withId(is(R.id.item_image))).perform(click()); 

Como todas las celdas de la cuadrícula tienen la misma identificación, obtuvo AmbiguousViewMatcherException . ¿Cómo solo recoger uno o alguno de ellos? ¡Gracias!

android.support.test.espresso.AmbiguousViewMatcherException: ‘con id: es ‘ coincide con múltiples vistas en la jerarquía. Las vistas de problemas están marcadas con ‘**** MATCHES ****’ a continuación.

+ ————-> ImageView {id = 2131493330, res-name = item_image, desc = Imagen, visibility = VISIBLE, ancho = 262, alto = 262, has-focus = falso, tiene -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 = 0.0, y = 0.0} **** PARTIDOS ****

+ ————-> ImageView {id = 2131493330, res-name = item_image, desc = Imagen, visibility = VISIBLE, ancho = 262, alto = 262, has-focus = falso, tiene -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 = 0.0, y = 0.0} **** PARTIDOS **** |

Debería usar onData() para operar en GridView :

 onData(withId(R.id.item_image)) .inAdapterView(withId(R.id.grid_adapter_id)) .atPosition(0) .perform(click()); 

Este código hará clic en la imagen dentro del primer elemento en GridView

Me sorprendió que no pudiera encontrar una solución simplemente proporcionando un índice junto con un matcher (es decir, withText, withId). La respuesta aceptada solo resuelve el problema cuando se trata de onData y ListViews.

Si tiene más de una vista en la pantalla con el mismo resId / text / contentDesc, puede elegir la que desea sin causar una AmbiguousViewMatcherException utilizando este comparador personalizado:

 public static Matcher withIndex(final Matcher matcher, final int index) { return new TypeSafeMatcher() { int currentIndex = 0; @Override public void describeTo(Description description) { description.appendText("with index: "); description.appendValue(index); matcher.describeTo(description); } @Override public boolean matchesSafely(View view) { return matcher.matches(view) && currentIndex++ == index; } }; } 

Por ejemplo:

 onView(withIndex(withId(R.id.my_view), 2)).perform(click()); 

realizará una acción de clic en la tercera instancia de R.id.my_view.

No está completamente relacionado con la situación de la vista de cuadrícula, pero puede usar hamcrest allOf matchers para combinar múltiples condiciones:

 import static org.hamcrest.CoreMatchers.allOf; onView(allOf(withId(R.id.login_password), withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .check(matches(isCompletelyDisplayed())) .check(matches(withHint(R.string.password_placeholder))); 

Creé un ViewMatcher que coincide con la primera vista que encuentra. Tal vez sea útil para alguien. Por ejemplo, cuando no tienes un AdapterView para usar onData () en.

 /** * Created by stost on 15.05.14. * Matches any view. But only on first match()-call. */ public class FirstViewMatcher extends BaseMatcher { public static boolean matchedBefore = false; public FirstViewMatcher() { matchedBefore = false; } @Override public boolean matches(Object o) { if (matchedBefore) { return false; } else { matchedBefore = true; return true; } } @Override public void describeTo(Description description) { description.appendText(" is the first view that comes along "); } @Factory public static  Matcher firstView() { return new FirstViewMatcher(); } } 

Úselo así:

  onView(FirstViewMatcher.firstView()).perform(click()); 

Intenté responder a @FrostRocket como parecía más prometedor, pero necesitaba agregar algunas personalizaciones:

 public static Matcher withIndex(final Matcher matcher, final int index) { return new TypeSafeMatcher() { int currentIndex; int viewObjHash; @SuppressLint("DefaultLocale") @Override public void describeTo(Description description) { description.appendText(String.format("with index: %d ", index)); matcher.describeTo(description); } @Override public boolean matchesSafely(View view) { if (matcher.matches(view) && currentIndex++ == index) { viewObjHash = view.hashCode(); } return view.hashCode() == viewObjHash; } }; } 

Casos:

 onView( withId( R.id.songListView ) ).perform( RealmRecyclerViewActions.scrollTo( Matchers.first(Matchers.withTextLabeled( "Love Song"))) ); onView( Matchers.first(withText( "Love Song")) ).perform( click() ); 

dentro de mi Matchers.class

 public static Matcher first(Matcher expected ){ return new TypeSafeMatcher() { private boolean first = false; @Override protected boolean matchesSafely(View item) { if( expected.matches(item) && !first ){ return first = true; } return false; } @Override public void describeTo(Description description) { description.appendText("Matcher.first( " + expected.toString() + " )" ); } }; }