如何使用Espresso点击 RecyclerView 项目中的特定视图?我知道我可以点击位置0的项目:
onView(withId(R.id.recyclerView))
.perform(RecyclerViewActions.actionOnItemAtPosition(0, click()));
但我需要点击该项目内的特定视图,而不是项目本身。
提前致谢。
- 编辑 -
更确切地说:我有一个 RecyclerView (R.id.recycler_view
)项目 CardView (R.id.card_view
)。在每个 CardView 里面,我有四个按钮(除其他外),我想点击一个特定的按钮(R.id.bt_deliver
)。
我想使用Espresso 2.0的新功能,但我不确定是否可行。
如果不可能,我想使用这样的东西(使用Thomas Keller代码):
onRecyclerItemView(R.id.card_view, ???, withId(R.id.bt_deliver)).perform(click());
但我不知道该在问号上放什么。
答案 0 :(得分:114)
您可以使用自定义视图操作来执行此操作。
public class MyViewAction {
public static ViewAction clickChildViewWithId(final int id) {
return new ViewAction() {
@Override
public Matcher<View> getConstraints() {
return null;
}
@Override
public String getDescription() {
return "Click on a child view with specified id.";
}
@Override
public void perform(UiController uiController, View view) {
View v = view.findViewById(id);
v.performClick();
}
};
}
}
然后你可以点击
onView(withId(R.id.rv_conference_list)).perform(
RecyclerViewActions.actionOnItemAtPosition(0, MyViewAction.clickChildViewWithId(R.id. bt_deliver)));
答案 1 :(得分:55)
现在使用android.support.test.espresso.contrib变得更容易了:
1)添加测试依赖性
androidTestCompile('com.android.support.test.espresso:espresso-contrib:2.0') {
exclude group: 'com.android.support', module: 'appcompat'
exclude group: 'com.android.support', module: 'support-v4'
exclude module: 'recyclerview-v7'
}
*排除3个模块,因为很可能你已经拥有它
2)然后做类似
的事情onView(withId(R.id.recycler_grid))
.perform(RecyclerViewActions.actionOnItemAtPosition(0, click()));
或者
onView(withId(R.id.recyclerView))
.perform(RecyclerViewActions.actionOnItem(
hasDescendant(withText("whatever")), click()));
或者
onView(withId(R.id.recycler_linear))
.check(matches(hasDescendant(withText("whatever"))));
答案 2 :(得分:5)
尝试下一步方法:
onView(withRecyclerView(R.id.recyclerView)
.atPositionOnView(position, R.id.bt_deliver))
.perform(click());
public static RecyclerViewMatcher withRecyclerView(final int recyclerViewId) {
return new RecyclerViewMatcher(recyclerViewId);
}
public class RecyclerViewMatcher {
final int mRecyclerViewId;
public RecyclerViewMatcher(int recyclerViewId) {
this.mRecyclerViewId = 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) {
int id = targetViewId == -1 ? mRecyclerViewId : targetViewId;
String idDescription = Integer.toString(id);
if (this.resources != null) {
try {
idDescription = this.resources.getResourceName(id);
} catch (Resources.NotFoundException var4) {
idDescription = String.format("%s (resource name not found)", id);
}
}
description.appendText("with id: " + idDescription);
}
public boolean matchesSafely(View view) {
this.resources = view.getResources();
if (childView == null) {
RecyclerView recyclerView =
(RecyclerView) view.getRootView().findViewById(mRecyclerViewId);
if (recyclerView != null) {
childView = recyclerView.findViewHolderForAdapterPosition(position).itemView;
}
else {
return false;
}
}
if (targetViewId == -1) {
return view == childView;
} else {
View targetView = childView.findViewById(targetViewId);
return view == targetView;
}
}
};
}
}
答案 3 :(得分:4)
这是我如何解决Kotlin中的问题:
fun clickOnViewChild(viewId: Int) = object : ViewAction {
override fun getConstraints() = null
override fun getDescription() = "Click on a child view with specified id."
override fun perform(uiController: UiController, view: View) = click().perform(uiController, view.findViewById<View>(viewId))
}
然后
onView(withId(R.id.recyclerView)).perform(RecyclerViewActions.actionOnItemAtPosition<RecyclerView.ViewHolder>(position, clickOnViewChild(R.id.viewToClickInTheRow)))
答案 4 :(得分:1)
您可以在recyclerView
的第三项上点击,如下所示:
onView(withId(R.id.recyclerView)).perform(
RecyclerViewActions.actionOnItemAtPosition<RecyclerView.ViewHolder>(2,click()))
不要忘记,以提供ViewHolder
类型,以使推理不会失败。
答案 5 :(得分:0)
上面的所有答案都对我不起作用,因此我建立了一个新方法,该方法搜索单元格内的所有视图以返回具有请求ID的视图。它需要两种方法(可以合并为一种):
fun performClickOnViewInCell(viewID: Int) = object : ViewAction {
override fun getConstraints(): org.hamcrest.Matcher<View> = click().constraints
override fun getDescription() = "Click on a child view with specified id."
override fun perform(uiController: UiController, view: View) {
val allChildViews = getAllChildrenBFS(view)
for (child in allChildViews) {
if (child.id == viewID) {
child.callOnClick()
}
}
}
}
private fun getAllChildrenBFS(v: View): List<View> {
val visited = ArrayList<View>();
val unvisited = ArrayList<View>();
unvisited.add(v);
while (!unvisited.isEmpty()) {
val child = unvisited.removeAt(0);
visited.add(child);
if (!(child is ViewGroup)) continue;
val group = child
val childCount = group.getChildCount();
for (i in 0 until childCount) { unvisited.add(group.getChildAt(i)) }
}
return visited;
}
然后最后,您可以通过执行以下操作在Recycler View上使用它:
onView(withId(R.id.recyclerView)).perform(actionOnItemAtPosition<RecyclerView.ViewHolder>(0, getViewFromCell(R.id.cellInnerView) {
val requestedView = it
}))
如果您想对视图进行其他操作,则可以使用回调函数返回视图,或者只需构建3-4个不同版本的视图即可执行其他任务。
答案 6 :(得分:0)
我一直在尝试各种方法来找出为什么@blade的答案对我不起作用的原因,只是意识到我有一个OnTouchListener()
,所以我相应地修改了ViewAction:
fun clickTopicToWeb(id: Int): ViewAction {
return object : ViewAction {
override fun getDescription(): String {...}
override fun getConstraints(): Matcher<View> {...}
override fun perform(uiController: UiController?, view: View?) {
view?.findViewById<View>(id)?.apply {
//Generalized for OnClickListeners as well
if(isEnabled && isClickable && !performClick()) {
//Define click event
val event: MotionEvent = MotionEvent.obtain(
SystemClock.uptimeMillis(),
SystemClock.uptimeMillis(),
MotionEvent.ACTION_UP,
view.x,
view.y,
0)
if(!dispatchTouchEvent(event))
throw Exception("Not clicking!")
}
}
}
}
}
答案 7 :(得分:0)
您甚至可以推广这种方法,以支持不仅click
的更多操作。这是我的解决方案:
fun <T : View> recyclerChildAction(@IdRes id: Int, block: T.() -> Unit): ViewAction {
return object : ViewAction {
override fun getConstraints(): Matcher<View> {
return any(View::class.java)
}
override fun getDescription(): String {
return "Performing action on RecyclerView child item"
}
override fun perform(
uiController: UiController,
view: View
) {
view.findViewById<T>(id).block()
}
}
}
然后对于EditText
,您可以执行以下操作:
onView(withId(R.id.yourRecyclerView))
.perform(
actionOnItemAtPosition<YourViewHolder>(
0,
recyclerChildAction<EditText>(R.id.editTextId) { setText("1000") }
)
)
答案 8 :(得分:-5)
首先给你的按钮提供唯一的内容描述,即“交付按钮第5行”。
<button android:contentDescription=".." />
然后滚动到行:
onView(withId(R.id.recycler_view)).perform(RecyclerViewActions.scrollToPosition(5));
然后根据contentDescription选择视图。
onView(withContentDescription("delivery button row 5")).perform(click());
内容说明是使用Espresso的onView并让您的应用更易于访问的绝佳方式。