Hoe kan ik Espresso gebruiken om op een specifieke weergave in een RecyclerView-item te klikken? Ik weet dat ik op het item op positie 0 kan klikken met:
onView(withId(R.id.recyclerView))
.perform(RecyclerViewActions.actionOnItemAtPosition(0, click()));
Maar ik moet op een specifieke weergave in dat item klikken en niet op het item zelf.
Bij voorbaat dank.
— bewerken —
Om precies te zijn: ik heb een RecyclerView(R.id.recycler_view
) waarvan de items CardView(R.id.card_view
). Binnen elke CardViewheb ik vier knoppen (onder andere) en ik wil op een specifieke knop klikken (R.id.bt_deliver
).
Ik wil graag de nieuwe functies van Espresso 2.0 gebruiken, maar ik weet niet zeker of dat mogelijk is.
Als dat niet mogelijk is, wil ik zoiets gebruiken (met Thomas Keller-code):
onRecyclerItemView(R.id.card_view, ???, withId(R.id.bt_deliver)).perform(click());
maar ik weet niet wat ik op de vraagtekens moet zetten.
Antwoord 1, autoriteit 100%
Je kunt het doen met de actie ‘Weergave aanpassen’.
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();
}
};
}
}
Dan kun je erop klikken met
onView(withId(R.id.rv_conference_list)).perform(
RecyclerViewActions.actionOnItemAtPosition(0, MyViewAction.clickChildViewWithId(R.id. bt_deliver)));
Antwoord 2, autoriteit 47%
Nu met android.support.test.espresso.contrib is het eenvoudiger geworden:
1)Testafhankelijkheid toevoegen
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'
}
*exclusief 3 modules, omdat je deze waarschijnlijk al hebt
2) Doe dan iets als
onView(withId(R.id.recycler_grid))
.perform(RecyclerViewActions.actionOnItemAtPosition(0, click()));
Of
onView(withId(R.id.recyclerView))
.perform(RecyclerViewActions.actionOnItem(
hasDescendant(withText("whatever")), click()));
Of
onView(withId(R.id.recycler_linear))
.check(matches(hasDescendant(withText("whatever"))));
Antwoord 3, autoriteit 9%
Hier is hoe ik het probleem in kotlin heb opgelost:
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))
}
en dan
onView(withId(R.id.recyclerView)).perform(RecyclerViewActions.actionOnItemAtPosition<RecyclerView.ViewHolder>(position, clickOnViewChild(R.id.viewToClickInTheRow)))
Antwoord 4, autoriteit 4%
Je kunt klikkenop derde itemvan recyclerView
als volgt:
onView(withId(R.id.recyclerView)).perform(
RecyclerViewActions.actionOnItemAtPosition<RecyclerView.ViewHolder>(2,click()))
Vergeet nietom het type ViewHolder
op te geven, zodat gevolgtrekking niet mislukt.
Antwoord 5, autoriteit 4%
Probeer de volgende aanpak:
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;
}
}
};
}
}
Antwoord 6
Je kunt deze aanpak zelfs veralgemenen om meer acties te ondersteunen, niet alleen click
. Hier is mijn oplossing hiervoor:
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()
}
}
}
En dan voor EditText
kun je zoiets als dit doen:
onView(withId(R.id.yourRecyclerView))
.perform(
actionOnItemAtPosition<YourViewHolder>(
0,
recyclerChildAction<EditText>(R.id.editTextId) { setText("1000") }
)
)
Antwoord 7
Alle antwoorden hierboven werkten niet voor mij, dus heb ik een nieuwe methode ontwikkeld die alle weergaven in een cel doorzoekt om de weergave met de gevraagde ID terug te geven. Het vereist twee methoden (kunnen worden gecombineerd tot één):
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;
}
Definitief kun je dit gebruiken in Recycler View door het volgende te doen:
onView(withId(R.id.recyclerView)).perform(actionOnItemAtPosition<RecyclerView.ViewHolder>(0, getViewFromCell(R.id.cellInnerView) {
val requestedView = it
}))
Je zou een callback kunnen gebruiken om de weergave terug te sturen als je er iets anders mee wilt doen, of je kunt er 3-4 verschillende versies van maken om andere taken uit te voeren.
Antwoord 8
Ik bleef verschillende methoden uitproberen om erachter te komen waarom het antwoord van @blade niet werkte voor mij, om me alleen maar te realiseren dat ik een OnTouchListener()
heb, heb ik de ViewAction dienovereenkomstig aangepast:
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!")
}
}
}
}
}
Antwoord 9
Geef uw knoppen eerst een unieke inhoudBeschrijvingen, d.w.z. “bezorgknoprij 5”.
<button android:contentDescription=".." />
Scroll vervolgens naar rij:
onView(withId(R.id.recycler_view)).perform(RecyclerViewActions.scrollToPosition(5));
Selecteer vervolgens de weergave op basis van contentDescription.
onView(withContentDescription("delivery button row 5")).perform(click());
Contentbeschrijving is een geweldige manier om Espresso’s onView te gebruiken en uw app toegankelijker te maken.