Android 使用 Espresso 在索引处选择子视图

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24748303/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 08:41:18  来源:igfitidea点击:

Selecting child view at index using Espresso

androidandroid-espresso

提问by ijunes

With Espresso when using a custom widget view with child image views, which Matcher type can I use to select the nth child? Example:

在 Espresso 中使用带有子图像视图的自定义小部件视图时,我可以使用哪种匹配器类型来选择第 n 个子视图?例子:

+--------->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}

回答by Maragues

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

To use it

使用它

onView(nthChildOf(withId(R.id.parent_container), 0)).check(matches(withText("I am the first child")));

回答by eski

To try and improve a little on Maragues's solution, I've made a few changes.

为了尝试改进 Maragues 的解决方案,我做了一些更改。

The solution is to create a custom Matcher<View>that wraps another Matcher<View>for the parent view, and takes the index of the child view to be matched.

解决方案是创建一个自定义Matcher<View>,它为父视图包装另一个Matcher<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("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);
        }
    };
}

Detailed Explanation

详细说明

You can override the describeTomethod in order to give an easy to understand description of the matcher by appending to the Descriptionargument. You'll also want to propagate the describeTocall to the parent matcher so it's description also gets added.

您可以覆盖describeTo方法,以便通过附加到Description参数来提供易于理解的匹配器描述。您还需要将describeTo调用传播到父匹配器,以便它的描述也被添加。

@Override
public void describeTo(Description description) {
    description.appendText("position " + childPosition + " of parent "); // Add this matcher's description.
    parentMatcher.describeTo(description); // Add the parentMatcher description.
}

Next, you should override matchesSafelywhich will determine when a match in the view hierarchy has been found. When called with a view whose parent matches the provided parent matcher, check that the view is equal to the child at the provided position.

接下来,您应该覆盖matchesSafely,它将确定何时在视图层次结构中找到匹配项。当使用父视图与提供的父匹配器匹配的视图调用时,检查视图是否等于提供位置的子视图。

If the parent doesn't have a childCountgreater than the child position, getChildAtwill return null and cause the test to crash. It's better to avoid crashing and allow the test to fail so that we get a proper test report and error message.

如果父级的childCount 不大于子级位置,则getChildAt将返回 null 并导致测试崩溃。最好避免崩溃并允许测试失败,以便我们获得正确的测试报告和错误消息。

@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.
}

回答by Nick Dong

If you can get the parent view. May be this linkwhich defined a matcher to get the first child of a view can give you some clue.

如果您可以获得父视图。可能是定义匹配器以获取视图的第一个子项的此链接可以为您提供一些线索。

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

            }
        };
    }