Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Espresso to click view inside RecyclerView item

People also ask

How do you scroll on recycler view espresso?

To interact with RecyclerViews using Espresso, you can use the espresso-contrib package, which has a collection of RecyclerViewActions that can be used to scroll to positions or to perform actions on items: scrollTo() - Scrolls to the matched View, if it exists.

How does recycler view work internally?

So, when we use RecyclerView, only six items are created at first, with five loaded at once to be displayed on-screen and one to be loaded. We now have 7 ViewHolders if we scroll down the list. One for the scrapped view, one for the to-be-loaded view, and five for the ones that are displayed.

Can we use RecyclerView inside RecyclerView in Android?

The RecyclerView widget manages the display and handling of items in a list. It provides Layout Managers to position these items. This way, you can create customized layout managers for RecyclerView containers. We can use a RecyclerView inside another RecyclerView.


You can do it with customize view action.

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

}

Then you can click it with

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

Now with android.support.test.espresso.contrib it has become easier:

1)Add test dependency

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

*exclude 3 modules, because very likely you already have it

2) Then do something like

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

Or

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

Or

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

Here is, how I resolved issue in 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))
}

and then

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

Try next approach:

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

            }
        };
    }
}

You can click on 3rd item of recyclerView Like this:

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

Do not forget to provide the ViewHolder type so that inference does not fail.


You can even generalize this approach to support more actions not only click. Here is my solution for this:

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

And then for EditText you can do something like this:

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