在Espresso中,如何select一个具有相同id的视图来避免AmbiguousViewMatcherException

有gridView有一些图像。 gridView的单元格从相同的预定义布局出来,它具有相同的id和desc。

R.id.item_image == 2131493330

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

由于网格中的所有单元格都有相同的id,因此得到AmbiguousViewMatcherException 。 如何挑选第一个或任何一个? 谢谢!

android.support.test.espresso.AmbiguousViewMatcherException:'with id:is <2131493330>'匹配层次结构中的多个视图。 问题视图在下方标有“**** MATCHES ****”。

+ ————-> ImageView {id = 2131493330,res-name = item_image,desc = Image,visibility = VISIBLE,width = 262,height = 262,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 = 0.0,y = 0.0} **** MATCHES ****

+ ————-> ImageView {id = 2131493330,res-name = item_image,desc = Image,visibility = VISIBLE,width = 262,height = 262,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 = 0.0,y = 0.0} **** MATCHES **** |

Solutions Collecting From Web of "在Espresso中,如何select一个具有相同id的视图来避免AmbiguousViewMatcherException"

您应该使用onData()GridView上进行操作:

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

这段代码将点击GridView第一项内的图像

我很惊讶,我找不到一个解决scheme,只需提供一个匹配与匹配器(即与文本,withId)。 接受的答案只能解决您在处理onData和ListViews时遇到的问题。

如果屏幕上有多个具有相同resId / text / contentDesc的视图,则可以使用此自定义匹配器select所需的一个,而不会导致AmbiguousViewMatcherException:

 public static Matcher<View> withIndex(final Matcher<View> matcher, final int index) { return new TypeSafeMatcher<View>() { 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; } }; } 

例如:

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

将对R.id.my_view的第三个实例执行一个单击操作。

不完全相关的网格视图的情况,但你可以使用hamcrest allOf匹配器结合多个条件:

 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))); 

我创build了一个ViewMatcher,它匹配它find的第一个视图。 也许这对别人有帮助。 例如,当你没有一个AdapterView使用onData()。

 /** * Created by stost on 15.05.14. * Matches any view. But only on first match()-call. */ public class FirstViewMatcher extends BaseMatcher<View> { 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 <T> Matcher<View> firstView() { return new FirstViewMatcher(); } } 

像这样使用它:

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

试过@FrostRocket答案是看起来最promissing,但需要添加一些定制:

 public static Matcher<View> withIndex(final Matcher<View> matcher, final int index) { return new TypeSafeMatcher<View>() { 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; } }; } 

案例:

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

在我的Matchers.class里面

 public static Matcher<View> first(Matcher<View> expected ){ return new TypeSafeMatcher<View>() { 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() + " )" ); } }; }