Verwenden von Espresso, um auf die Ansicht innerhalb des RecyclerView-Elements zu klicken

Lesezeit: 7 Minuten

Benutzer-Avatar
Philipp Ramos

Wie kann ich mit Espresso auf eine bestimmte Ansicht in a klicken RecyclerView Artikel? Ich weiß, dass ich das Element an Position 0 anklicken kann mit:

onView(withId(R.id.recyclerView))
.perform(RecyclerViewActions.actionOnItemAtPosition(0, click()));

Aber ich muss auf eine bestimmte Ansicht innerhalb dieses Elements klicken und nicht auf das Element selbst.

Danke im Voraus.

— bearbeiten —

Genauer gesagt: Ich habe eine RecyclerView (R.id.recycler_view) welche Artikel sind CardView (R.id.card_view). In jedem CardView Ich habe (unter anderem) vier Schaltflächen und möchte auf eine bestimmte Schaltfläche klicken (R.id.bt_deliver).

Ich möchte die neuen Funktionen von Espresso 2.0 nutzen, bin mir aber nicht sicher, ob das möglich ist.

Wenn dies nicht möglich ist, möchte ich so etwas verwenden (mit Thomas Keller-Code):

onRecyclerItemView(R.id.card_view, ???, withId(R.id.bt_deliver)).perform(click());

aber ich weiß nicht, was ich auf die Fragezeichen schreiben soll.

  • Überprüfen Sie dies

    – Skizo-ozᴉʞS

    12. Februar 2015 um 11:41 Uhr

  • Hallo Panzer für die schnelle Antwort! 🙂 Ich habe diese Frage gesehen, aber ich kann keine Hilfe zur Verwendung finden RecyclerViewActions zu tun, was ich will. Soll ich die verwenden Alte Antwort? Vielen Dank.

    – Philippe Ramos

    12. Februar 2015 um 11:59 Uhr


  • Haben Sie eine Lösung für Ihr Problem gefunden?

    – HowieH

    7. April 2015 um 12:52 Uhr

  • @HowieH: Nein. Ich habe aufgegeben und benutze jetzt Robotium …

    – Philippe Ramos

    7. April 2015 um 13:47 Uhr

Benutzer-Avatar
Klinge

Sie können dies mit der Aktion Ansicht anpassen tun.

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

}

Dann kannst du es mit anklicken

onView(withId(R.id.rv_conference_list)).perform(
            RecyclerViewActions.actionOnItemAtPosition(0, MyViewAction.clickChildViewWithId(R.id. bt_deliver)));

  • Ich würde die Nullprüfung entfernen, damit, wenn die untergeordnete Ansicht nicht gefunden wird, eine Ausnahme ausgelöst wird, anstatt stillschweigend nichts zu tun.

    – Alvaro Gutiérrez Perez

    9. Mai 2016 um 13:46 Uhr

  • Danke für die Antwort. Aber in einem Problem geschlagen. Wenn ich in der Funktion onPerform() die Funktion onView(withId()) verwendet habe, wird die Funktion ausgelöst. Was ist der Grund für dieses Verhalten?

    – der dunkle Passagier

    15. September 2016 um 10:23 Uhr

  • funktioniert mit espresso:espresso-contrib:2.2.2 aber nicht mit 2.2.1 irgendwelche Gründe warum? Ich habe einige Abhängigkeiten, aufgrund derer ich 2.2.2 nicht verwenden kann.

    – RosAng

    26. September 2016 um 18:27 Uhr

  • Ich habe damit ursprünglich eine NullPointerException bekommen, also musste ich getConstraints() implementieren, um das zu vermeiden. Folgendes hat bei mir funktioniert: public Matcher getConstraints() { return isAssignableFrom(View.class); }

    – dfinn

    9. Dezember 2016 um 18:09 Uhr


  • Die obige Lösung funktioniert bei mir nicht. Ich erhalte die folgende Fehlermeldung: android.support.test.espresso.PerformException: Fehler beim Ausführen von „android.support.test.espres[email protected]fad9df“ in der Ansicht „mit der ID: adamhurwitz.github.io.doordashlite :id/recyclerView’.

    – Adam Hurwitz

    14. Juli 2017 um 5:17 Uhr

Mit android.support.test.espresso.contrib ist es jetzt einfacher geworden:

1) Testabhängigkeit hinzufügen

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 Module ausschließen, da Sie es sehr wahrscheinlich bereits haben

2) Dann tun Sie so etwas wie

onView(withId(R.id.recycler_grid))
            .perform(RecyclerViewActions.actionOnItemAtPosition(0, click()));

Oder

onView(withId(R.id.recyclerView))
  .perform(RecyclerViewActions.actionOnItem(
            hasDescendant(withText("whatever")), click()));

Oder

onView(withId(R.id.recycler_linear))
            .check(matches(hasDescendant(withText("whatever"))));

  • Danke, das Nichtausschließen dieser Module führt bei mir zu einem java.lang.IncompatibleClassChangeError.

    – Junge

    17. Februar 2016 um 15:40 Uhr

  • und was ist zu tun, um sagen wir mal im 5. Punkt der Liste eine bestimmte Ansicht anzuklicken? In den bereitgestellten Beispielen sehe ich nur, wie man auf das 5. Element klickt oder auf und Element mit untergeordneter Ansicht klickt, aber nicht beide zusammen, oder habe ich etwas übersehen?

    – donfuxx

    24. Mai 2016 um 15:51 Uhr

  • ich musste es machen exclude group: 'com.android.support' exclude module: 'recyclerview-v7'

    – Jemshit Iskenderov

    21. Dezember 2016 um 14:20 Uhr

  • Es ist nutzlos, wenn Sie in RecyclerView auf das Kontrollkästchen klicken möchten.

    – Farid

    11. März 2018 um 10:52 Uhr

  • In Kotlin actionOnItemAtPosition erfordert einen Typparameter. Ich bin mir nicht sicher, was ich dort einfügen soll.

    – Stephen__T

    1. Januar 2019 um 1:53 Uhr

Hier ist, wie ich das Problem in Kotlin gelöst habe:

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

und dann

onView(withId(R.id.recyclerView)).perform(RecyclerViewActions.actionOnItemAtPosition<RecyclerView.ViewHolder>(position, clickOnViewChild(R.id.viewToClickInTheRow)))

  • Ich bin auf E/TestRunner gestoßen: java.lang.NullPointerException: Attempt to invoke virtual method ‘void android.view.View.getLocationOnScreen(int[])’ bei einer Nullobjektreferenz; eine Ahnung warum?

    – sagvortana

    18. Februar 2020 um 17:41 Uhr

  • Ich bin auf das gleiche Problem gestoßen.

    – Casey Perkins

    22. Oktober 2021 um 18:42 Uhr

  • Danke, schöne Lösung. Funktioniert super!

    – Juri Popiw

    1. Februar um 12:56 Uhr

Du kannst klicken an 3. Artikel von recyclerView So was:

onView(withId(R.id.recyclerView)).perform(
                RecyclerViewActions.actionOnItemAtPosition<RecyclerView.ViewHolder>(2,click()))

Tun nicht vergessen die bereitzustellen ViewHolder Typ, damit die Inferenz nicht fehlschlägt.

Versuchen Sie den nächsten Ansatz:

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

            }
        };
    }
}

Benutzer-Avatar
Jokubas Trinkunas

Sie können diesen Ansatz sogar verallgemeinern, um nicht nur mehr Aktionen zu unterstützen click. Hier ist meine Lösung dafür:

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

Und dann für EditText Sie können so etwas tun:

onView(withId(R.id.yourRecyclerView))
        .perform(
            actionOnItemAtPosition<YourViewHolder>(
                0,
                recyclerChildAction<EditText>(R.id.editTextId) { setText("1000") }
            )
        )

Benutzer-Avatar
paul_f

Alle obigen Antworten haben bei mir nicht funktioniert, daher habe ich eine neue Methode erstellt, die alle Ansichten in einer Zelle durchsucht, um die Ansicht mit der angeforderten ID zurückzugeben. Es erfordert zwei Methoden (könnte zu einer kombiniert werden):

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

Abschließend können Sie dies in der Recycler-Ansicht verwenden, indem Sie Folgendes tun:

onView(withId(R.id.recyclerView)).perform(actionOnItemAtPosition<RecyclerView.ViewHolder>(0, getViewFromCell(R.id.cellInnerView) {
            val requestedView = it
}))

Sie können einen Rückruf verwenden, um die Ansicht zurückzugeben, wenn Sie etwas anderes damit machen möchten, oder einfach 3-4 verschiedene Versionen davon erstellen, um andere Aufgaben zu erledigen.

1333210cookie-checkVerwenden von Espresso, um auf die Ansicht innerhalb des RecyclerView-Elements zu klicken

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy