Espressoを使用してRecyclerViewアイテム内のビューをクリックする


100

Espressoを使用してRecyclerViewアイテム内の特定のビューをクリックするにはどうすればよいですか?次を使用して、位置0の項目をクリックできることを知っています。

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

しかし、アイテム自体ではなく、そのアイテム内の特定のビューをクリックする必要があります。

前もって感謝します。

-編集-

より正確に言うと、私はRecyclerViewR.id.recycler_view)を持ち、どのアイテムがCardViewR.id.card_view)であるかを示しています。各CardViewの内部には4つのボタンがあり(特に)、特定のボタン(R.id.bt_deliver)をクリックします。

Espresso 2.0の新機能を使用したいのですが、それが可能かどうかはわかりません。

それが不可能な場合は、次のようなものを使用します(Thomas Kellerコードを使用)。

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

しかし、疑問符に何を付けるかわかりません。


チェックこれを
Skizo-ozᴉʞS

1
こんにちは、迅速な回答のための戦車!:-)その質問を見たことがありますが、RecyclerViewActionsを使用して自分のやりたいことを行う方法についてヘルプが見つかりません。古い回答を使用する必要がありますか?ありがとう。
Filipe Ramos、

問題の解決策を見つけましたか?
HowieH 2015

1
@HowieH:いいえ。私はあきらめ、現在Robotiumを使用しています...
フィリペラモス

回答:


136

あなたはビューアクションをカスタマイズすることでそれを行うことができます。

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

2
私はnullチェックを削除するので、子ビューが見つからない場合は何も通知せずに例外がスローされます。
アルバログティエレスペレス

答えてくれてありがとう。しかし、一つの問題にぶつかった。onPerform()関数で、onView(withId())関数を使用すると、関数が実行されます。この動作の理由は何ですか?
thedarkpassenger 2016

3
espresso:espresso-contrib:2.2.2では機能しますが、2.2.1では機能しません。理由は何ですか。2.2.2を使用できないため、いくつかの依存関係があります。
RosAng

3
もともとこれでNullPointerExceptionが発生していたため、それを回避するためにgetConstraints()を実装する必要がありました。次は私のために働きました:public Matcher <View> getConstraints(){return isAssignableFrom(View.class); }
dfinn 16

上記の解決策は私のために働いていません。次のエラーが発生します:android.support.test.espresso.PerformException:エラー「id:adamhurwitz.github.io.doordashliteを含むビューで「android.support.test.espresso.contrib.RecyclerViewActions$ActionOnItemAtPositionViewAction@fad9df」を実行中にエラーが発生しました:id / recyclerView '。
アダムフルヴィッツ2017

66

これで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"))));

1
おかげで、これらのモジュールを除外しないと、java.lang.IncompatibleClassChangeErrorが発生します。
hboy

8
リストの5番目のアイテムで特定のビューを言うとしましょう。提供されている例では、5番目のアイテムまたは子孫ビューのアイテムをクリックする方法のみが表示されますが、両方が一緒に表示されるわけではありません。または、何かを見逃しましたか?
donfuxx 2016年

1
私はしなければなりませんexclude group: 'com.android.support' exclude module: 'recyclerview-v7'
でした

1
RecyclerView内のチェックボックスをクリックしたい場合は無意味です。
ファリッド2018年

2
kotlinでactionOnItemAtPositiontypeのparamが必要です。そこに何を置くかわからない。
ZeroDivide、

7

ここに、私が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)))

E / TestRunner:java.lang.NullPointerException:仮想メソッド 'void android.view.View.getLocationOnScreen(int [])'をnullオブジェクト参照で呼び出そうとしました。なぜか?
sayvortana

6

次のアプローチを試してください:

    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

あなたは、することができますクリック3番目の項目recyclerViewこのように:

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

くださいを忘れないで提供するために、ViewHolder推論が失敗しないように、タイプを。


1
欠席し<RecyclerView.ViewHolder>ましたありがとう;)
Skizo-ozᴉʞS19年

0

上記のすべての回答が機能しなかったため、セル内のすべてのビューを検索して、要求されたIDのビューを返す新しいメソッドを作成しました。2つの方法が必要です(1つに組み合わせることができます)。

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の異なるバージョンを作成して他のタスクを実行できます。


0

私はさまざまな方法を試し続けて、@ bladeの答えが私にとってうまくいかない理由を見つけましたOnTouchListener()

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

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

-5

最初にボタンに一意のcontentDescriptions、つまり「配信ボタンの行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を使用してアプリをよりアクセシブルにする優れた方法です。


5
内容説明はすべきではない CDは、おそらく「注文<商品概要>のようなものである必要があり、このために-それはa11yニーズを持つユーザーのための技術やデバッグ情報を噴出するためのビューのためのアプリをよりアクセシブルしない-ような方法で使用することボタン"。withText("Order")も機能し、テスト時に注文内容を知る必要はありません。
ataulm
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.