使用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}
答案 0 :(得分:58)
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);
}
};
}
使用它
onView(nthChildOf(withId(R.id.parent_container), 0)).check(matches(withText("I am the first child")));
答案 1 :(得分:21)
为了尝试改进Maragues的解决方案,我做了一些改变。
解决方案是创建一个自定义匹配器&lt; View&gt; ,为父视图包装另一个 Matcher&lt; View&gt; ,并将子视图的索引设为匹配。
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);
}
};
}
详细说明
您可以覆盖 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.
}
接下来,您应该覆盖 matchesSafely ,这将确定何时找到视图层次结构中的匹配项。当使用其父匹配提供的父匹配器的视图调用时,请检查视图是否等于提供位置处的子视图。
如果父级没有 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.
}
答案 2 :(得分:11)
如果您可以获得父视图。可能是this link定义了一个匹配器来获取视图的第一个孩子可能会给你一些线索。
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);
}
};
}
答案 3 :(得分:2)
尽管此线程中的答案确实有用,但我只是想指出,有可能无需定义新的Matcher
类就可以获取特定视图的特定子句柄。
您可以通过将Espresso提供的视图匹配器合并为以下方法来实现:
/**
* @param parentViewId the resource id of the parent [View].
* @param position the child index of the [View] to match.
* @return a [Matcher] that matches the child [View] which has the given [position] within the specified parent.
*/
fun withPositionInParent(parentViewId: Int, position: Int): Matcher<View> {
return allOf(withParent(withId(parentViewId)), withParentIndex(position))
}
然后按如下所示使用此方法:
onView(
withPositionInParent(R.id.parent, 0)
).check(
matches(withId(R.id.child))
)