在Espresso中,如何选择具有相同id的视图以避免AmbiguousViewMatcherException

lan*_*nyf 44 android android-espresso

有gridView有一些图像.gridView的单元格来自相同的预定义布局,具有相同的id和desc.

R.id.item_image == 2131493330

onView(withId(is(R.id.item_image))).perform(click());
Run Code Online (Sandbox Code Playgroud)

由于网格中的所有单元都具有相同的id,因此它得到了AmbiguousViewMatcherException.如何选择第一个或其中任何一个?谢谢!

android.support.test.espresso.AmbiguousViewMatcherException:'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****|

Fro*_*ket 81

我很惊讶我只是通过简单地提供索引和匹配器(即withText,withId)找不到解决方案.当你处理onData和ListViews时,接受的答案只能解决问题.

如果屏幕上有多个具有相同resId/text/contentDesc的视图,则可以使用此自定义匹配器选择所需的视图而不会导致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;
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

例如:

onView(withIndex(withId(R.id.my_view), 2)).perform(click());
Run Code Online (Sandbox Code Playgroud)

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

  • 这是一个很好的解决方案,我一直在寻找这个,因为我的视图不是适配器视图,我们使用的是粘性列表视图,它实现了没有列表视图的列表。 (3认同)
  • “Matcher”合约规定“Matcher”应该是无状态的。这个“Matcher”将会以微妙的方式崩溃。 (2认同)

den*_*nys 24

您应该使用onData()以下操作GridView:

onData(withId(R.id.item_image))
        .inAdapterView(withId(R.id.grid_adapter_id))
        .atPosition(0)
        .perform(click());
Run Code Online (Sandbox Code Playgroud)

此代码将单击第一项内的图像 GridView

  • @NeonWarge,是的,GridView的id或者也可以是ListView.但不是Recycler View.你不能用`onData()`. (3认同)

Shu*_*ary 21

与网格视图情况不完全相关,但您可以使用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)));
Run Code Online (Sandbox Code Playgroud)


Ste*_*nTo 13

我创建了一个ViewMatcher,它匹配它找到的第一个视图.也许这对某人有帮助.例如,当您没有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();
   }
}
Run Code Online (Sandbox Code Playgroud)

像这样使用它:

 onView(FirstViewMatcher.firstView()).perform(click());
Run Code Online (Sandbox Code Playgroud)

  • @wapples取决于......如果你想匹配一个视图类型和从它继承的所有类型,你可以在matches() - methode中使用类似`if(o instanceof ClassToMatch.class)`的东西.如果你只想匹配一个特定的类,你可以做一些像`if("ClassToMatchAsString".equals(o.getClass().getName()))`或`if("ClassToMatchAsString".equals(o.getClass()) .getSimpleName()))`.或者,如果您根本不想编辑代码,这也应该起作用:`onView(allOf(withClassName(endsWith("ClassToMatchAsString")),FirstViewMatcher.firstView())).perform(click());` (2认同)

fad*_*a21 12

尝试@FrostRocket回答看起来最有希望,但需要添加一些自定义:

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;
        }
    };
}
Run Code Online (Sandbox Code Playgroud)


Jua*_*dez 8

案例:

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

在我的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() + " )" );
        }
    };
}
Run Code Online (Sandbox Code Playgroud)


Fra*_*pos 7

同样与网格视图没有特别关系,但是如果它对其他人有用,我也会遇到类似的问题,即RecyclerView根布局上的元素都具有相同的ID,并且 显示在屏幕上。帮助我解决的是检查,descendancy例如:

 onView(allOf(withId(R.id.my_view), not(isDescendantOfA(withId(R.id.recyclerView))))).check(matches(withText("My Text")));
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明