如何检查特定 RecyclerView 项目上的视图是否可见?
How to check if a View is VISIBLE on a specific RecyclerView item?
我刚开始使用浓缩咖啡。我无法真正得到我想要使用此代码测试的内容:
onData(withId(R.id.relativelayout_tag))
.inAdapterView(withId(R.id.recyclerview_tag_list))
.onChildView(withId(R.id.imageview_tag))
.atPosition(1)
.check(matches(isDisplayed()));
其中 R.id.imageview_tag
是 R.id.relativelayout_tag
的子代。 R.id.relativelayout_tag
包含我的适配器项目的全部内容。 R.id.recyclerview_tag_list
是我的 RecyclerView
的名字,我在上面分配了一个特定的 RecyclerView
Adapter
.
这是一个非常非常基础的测试。以下是用户程序:
- Select, SPECIFICALLY,
RecyclerView
上的第一项(我真的不在乎什么
文本在视图上)。也不建议使用视图文本来标识第一项。 我不关心适配器项目的内容,甚至不关心在某些视图上放置唯一标签。
- 在 select 上,指示器视图(显示该项目的 ImageView
selected) 应该出现。
非常基础和简单。使用 Espresso 为这个基本用户故事编写测试非常困难。当我 运行 该特定测试时,它总是失败说明:
Caused by: java.lang.RuntimeException: Action will not be performed because the target view does not match one or more of the following constraints:
(is assignable from class: class android.widget.AdapterView and is displayed on the screen to the user)
Target view: "RecyclerView{id=2131624115, res-name=recyclerview_tag_list, visibility=VISIBLE, width=480, height=1032, has-focus=false, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=true, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=15}"
这没有意义,因为我已经看到了列表。我什至可以 运行 这个测试就好了:
onView(withId(R.id.recyclerview_tag_list))
.perform(RecyclerViewActions
.actionOnItemAtPosition(1, click()));
完整测试如下:
@Test
public void shouldTagToggleSelected()
{
onView(withId(R.id.recyclerview_tag_list))
.perform(RecyclerViewActions
.actionOnItemAtPosition(1, click()));
onData(withId(R.id.relativelayout_tag))
.inAdapterView(withId(R.id.recyclerview_tag_list))
.onChildView(withId(R.id.imageview_tag))
.atPosition(1)
.check(matches(isDisplayed()));
//onView(withId(R.id.imageview_tag))
// .check(matches(isDisplayed()));
}
如果指示器视图的可见性设置为 visible
仅针对该特定项目(或我选择的任何项目),我想测试的内容。
有什么想法吗?也许我很想念什么。
非常感谢!
onData
不适用于 RecyclerView
,因为 RecyclerView
不扩展 AdapterView
。
您需要使用 onView
来做出您的断言。如果它是回收站视图中的第一项,那么您可以使用类似这样的匹配器来进行断言:
public static Matcher<View> withViewAtPosition(final int position, final Matcher<View> itemMatcher) {
return new BoundedMatcher<View, RecyclerView>(RecyclerView.class) {
@Override
public void describeTo(Description description) {
itemMatcher.describeTo(description);
}
@Override
protected boolean matchesSafely(RecyclerView recyclerView) {
final RecyclerView.ViewHolder viewHolder = recyclerView.findViewHolderForAdapterPosition(position);
return viewHolder != null && itemMatcher.matches(viewHolder.itemView);
}
};
}
用法如下:
onView(withId(R.id.recyclerview_tag_list))
.check(matches(withViewAtPosition(1, hasDescendant(allOf(withId(R.id.imageview_tag), isDisplayed())))));
请记住,如果您的 ViewHolder 尚未布局,此匹配器将失败。如果是这种情况,您需要使用 RecyclerViewActions 滚动到 ViewHolder。如果在使用匹配器之前单击测试中的项目,则无需滚动。
我在检查不可见的元素线性布局时出现异常错误。要解决它,我必须添加 withFailureHandler 如下:
class MyTest {
boolean isViewVisible = true;
public void test1(){
onView(withRecyclerView(R.id.recycler_view)
.atPositionOnView(index, R.id.linear_layout))
.withFailureHandler(new FailureHandler() {
@Override
public void handle(Throwable error, Matcher<View> viewMatcher){
isViewVisible = false;
}
})
.check(isVisible());
//wait for failure handler to finish
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(isViewVisible){
//do something
}
}
}
class MyTestUtil {
public static RecyclerViewMatcher withRecyclerView(final int recyclerViewId) {
return new RecyclerViewMatcher(recyclerViewId);
}
public static ViewAssertion isVisible() {
return new ViewAssertion() {
public void check(View view, NoMatchingViewException noView) {
assertThat(view, new VisibilityMatcher(View.VISIBLE));
}
};
}
}
public class RecyclerViewMatcher {
private final int recyclerViewId;
public RecyclerViewMatcher(int recyclerViewId) {
this.recyclerViewId = recyclerViewId;
}
public Matcher<View> atPosition(final int position) {
return atPositionOnView(position, -1);
}
public Matcher<View> atPositionOnView(final int position, final int targetViewId) {
return new TypeSafeMatcher<View>() {
Resources resources = null;
View childView;
public void describeTo(Description description) {
String idDescription = Integer.toString(recyclerViewId);
if (this.resources != null) {
try {
idDescription = this.resources.getResourceName(recyclerViewId);
} catch (Resources.NotFoundException var4) {
idDescription = String.format("%s (resource name not found)",
new Object[] { Integer.valueOf
(recyclerViewId) });
}
}
description.appendText("with id: " + idDescription);
}
public boolean matchesSafely(View view) {
this.resources = view.getResources();
if (childView == null) {
RecyclerView recyclerView =
(RecyclerView) view.getRootView().findViewById(recyclerViewId);
if (recyclerView != null && recyclerView.getId() == recyclerViewId) {
childView = recyclerView.findViewHolderForAdapterPosition(position).itemView;
}
else {
return false;
}
}
if (targetViewId == -1) {
return view == childView;
} else {
View targetView = childView.findViewById(targetViewId);
return view == targetView;
}
}
};
}
}
我刚开始使用浓缩咖啡。我无法真正得到我想要使用此代码测试的内容:
onData(withId(R.id.relativelayout_tag))
.inAdapterView(withId(R.id.recyclerview_tag_list))
.onChildView(withId(R.id.imageview_tag))
.atPosition(1)
.check(matches(isDisplayed()));
其中 R.id.imageview_tag
是 R.id.relativelayout_tag
的子代。 R.id.relativelayout_tag
包含我的适配器项目的全部内容。 R.id.recyclerview_tag_list
是我的 RecyclerView
的名字,我在上面分配了一个特定的 RecyclerView
Adapter
.
这是一个非常非常基础的测试。以下是用户程序:
- Select, SPECIFICALLY,
RecyclerView
上的第一项(我真的不在乎什么 文本在视图上)。也不建议使用视图文本来标识第一项。 我不关心适配器项目的内容,甚至不关心在某些视图上放置唯一标签。 - 在 select 上,指示器视图(显示该项目的 ImageView selected) 应该出现。
非常基础和简单。使用 Espresso 为这个基本用户故事编写测试非常困难。当我 运行 该特定测试时,它总是失败说明:
Caused by: java.lang.RuntimeException: Action will not be performed because the target view does not match one or more of the following constraints:
(is assignable from class: class android.widget.AdapterView and is displayed on the screen to the user)
Target view: "RecyclerView{id=2131624115, res-name=recyclerview_tag_list, visibility=VISIBLE, width=480, height=1032, has-focus=false, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=true, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=15}"
这没有意义,因为我已经看到了列表。我什至可以 运行 这个测试就好了:
onView(withId(R.id.recyclerview_tag_list))
.perform(RecyclerViewActions
.actionOnItemAtPosition(1, click()));
完整测试如下:
@Test
public void shouldTagToggleSelected()
{
onView(withId(R.id.recyclerview_tag_list))
.perform(RecyclerViewActions
.actionOnItemAtPosition(1, click()));
onData(withId(R.id.relativelayout_tag))
.inAdapterView(withId(R.id.recyclerview_tag_list))
.onChildView(withId(R.id.imageview_tag))
.atPosition(1)
.check(matches(isDisplayed()));
//onView(withId(R.id.imageview_tag))
// .check(matches(isDisplayed()));
}
如果指示器视图的可见性设置为 visible
仅针对该特定项目(或我选择的任何项目),我想测试的内容。
有什么想法吗?也许我很想念什么。
非常感谢!
onData
不适用于 RecyclerView
,因为 RecyclerView
不扩展 AdapterView
。
您需要使用 onView
来做出您的断言。如果它是回收站视图中的第一项,那么您可以使用类似这样的匹配器来进行断言:
public static Matcher<View> withViewAtPosition(final int position, final Matcher<View> itemMatcher) {
return new BoundedMatcher<View, RecyclerView>(RecyclerView.class) {
@Override
public void describeTo(Description description) {
itemMatcher.describeTo(description);
}
@Override
protected boolean matchesSafely(RecyclerView recyclerView) {
final RecyclerView.ViewHolder viewHolder = recyclerView.findViewHolderForAdapterPosition(position);
return viewHolder != null && itemMatcher.matches(viewHolder.itemView);
}
};
}
用法如下:
onView(withId(R.id.recyclerview_tag_list))
.check(matches(withViewAtPosition(1, hasDescendant(allOf(withId(R.id.imageview_tag), isDisplayed())))));
请记住,如果您的 ViewHolder 尚未布局,此匹配器将失败。如果是这种情况,您需要使用 RecyclerViewActions 滚动到 ViewHolder。如果在使用匹配器之前单击测试中的项目,则无需滚动。
我在检查不可见的元素线性布局时出现异常错误。要解决它,我必须添加 withFailureHandler 如下:
class MyTest {
boolean isViewVisible = true;
public void test1(){
onView(withRecyclerView(R.id.recycler_view)
.atPositionOnView(index, R.id.linear_layout))
.withFailureHandler(new FailureHandler() {
@Override
public void handle(Throwable error, Matcher<View> viewMatcher){
isViewVisible = false;
}
})
.check(isVisible());
//wait for failure handler to finish
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(isViewVisible){
//do something
}
}
}
class MyTestUtil {
public static RecyclerViewMatcher withRecyclerView(final int recyclerViewId) {
return new RecyclerViewMatcher(recyclerViewId);
}
public static ViewAssertion isVisible() {
return new ViewAssertion() {
public void check(View view, NoMatchingViewException noView) {
assertThat(view, new VisibilityMatcher(View.VISIBLE));
}
};
}
}
public class RecyclerViewMatcher {
private final int recyclerViewId;
public RecyclerViewMatcher(int recyclerViewId) {
this.recyclerViewId = recyclerViewId;
}
public Matcher<View> atPosition(final int position) {
return atPositionOnView(position, -1);
}
public Matcher<View> atPositionOnView(final int position, final int targetViewId) {
return new TypeSafeMatcher<View>() {
Resources resources = null;
View childView;
public void describeTo(Description description) {
String idDescription = Integer.toString(recyclerViewId);
if (this.resources != null) {
try {
idDescription = this.resources.getResourceName(recyclerViewId);
} catch (Resources.NotFoundException var4) {
idDescription = String.format("%s (resource name not found)",
new Object[] { Integer.valueOf
(recyclerViewId) });
}
}
description.appendText("with id: " + idDescription);
}
public boolean matchesSafely(View view) {
this.resources = view.getResources();
if (childView == null) {
RecyclerView recyclerView =
(RecyclerView) view.getRootView().findViewById(recyclerViewId);
if (recyclerView != null && recyclerView.getId() == recyclerViewId) {
childView = recyclerView.findViewHolderForAdapterPosition(position).itemView;
}
else {
return false;
}
}
if (targetViewId == -1) {
return view == childView;
} else {
View targetView = childView.findViewById(targetViewId);
return view == targetView;
}
}
};
}
}