νƒœκ·Έ 보관물: android-recyclerview

android-recyclerview

Espressoλ₯Ό μ‚¬μš©ν•˜μ—¬ RecyclerView ν•­λͺ© 내뢀보기 클릭 ν•­λͺ© μžμ²΄κ°€ μ•„λ‹Œ ν•΄λ‹Ή ν•­λͺ© λ‚΄λΆ€μ˜ νŠΉμ •λ³΄κΈ°λ₯Ό

Espressoλ₯Ό μ‚¬μš©ν•˜μ—¬ RecyclerView ν•­λͺ© λ‚΄μ˜ νŠΉμ •λ³΄κΈ°λ₯Ό ν΄λ¦­ν•˜λ €λ©΄ μ–΄λ–»κ²Œ ν•΄μ•Όν•©λ‹ˆκΉŒ? λ‹€μŒμ„ μ‚¬μš©ν•˜μ—¬ μœ„μΉ˜ 0μ—μ„œ ν•­λͺ©μ„ 클릭 ν•  수 μžˆλ‹€λŠ” 것을 μ•Œκ³  μžˆμŠ΅λ‹ˆλ‹€.

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

ν•˜μ§€λ§Œ ν•­λͺ© μžμ²΄κ°€ μ•„λ‹Œ ν•΄λ‹Ή ν•­λͺ© λ‚΄λΆ€μ˜ νŠΉμ •λ³΄κΈ°λ₯Ό ν΄λ¦­ν•΄μ•Όν•©λ‹ˆλ‹€.

미리 κ°μ‚¬λ“œλ¦½λ‹ˆλ‹€.

β€” νŽΈμ§‘ν•˜λ‹€ β€”

더 μ •ν™•ν•˜κ²Œ λ§ν•˜μžλ©΄, CardView ( ) ν•­λͺ© 인 RecyclerView ( R.id.recycler_view)κ°€ μžˆμŠ΅λ‹ˆλ‹€ . 각 CardView μ•ˆμ—λŠ” 4 개의 λ²„νŠΌ (λ‹€λ₯Έ 것듀 μ€‘μ—μ„œ)이 있고 νŠΉμ • λ²„νŠΌ ( ) 을 ν΄λ¦­ν•˜κ³  μ‹ΆμŠ΅λ‹ˆλ‹€ .R.id.card_viewR.id.bt_deliver

Espresso 2.0의 μƒˆλ‘œμš΄ κΈ°λŠ₯을 μ‚¬μš©ν•˜κ³  μ‹Άμ§€λ§Œ 이것이 κ°€λŠ₯ν•œμ§€ 잘 λͺ¨λ₯΄κ² μŠ΅λ‹ˆλ‹€.

κ°€λŠ₯ν•˜μ§€ μ•Šλ‹€λ©΄ λ‹€μŒκ³Ό 같이 μ‚¬μš©ν•˜κ³  μ‹ΆμŠ΅λ‹ˆλ‹€ (Thomas Keller μ½”λ“œ μ‚¬μš©).

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

ν•˜μ§€λ§Œ λ‚˜λŠ” λ¬ΌμŒν‘œμ— 무엇을 써야할지 λͺ¨λ₯΄κ² μŠ΅λ‹ˆλ‹€.



λ‹΅λ³€

보기 μž‘μ—…μ„ μ‚¬μš©μž μ§€μ •ν•˜μ—¬ μˆ˜ν–‰ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

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

λ‹΅λ³€

이제 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"))));

λ‹΅λ³€

λ‹€μŒμ€ 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)))

λ‹΅λ³€

λ‹€μŒ μ ‘κ·Ό 방식을 μ‹œλ„ν•˜μ‹­μ‹œμ˜€.

    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 ν•­λͺ© 의 recyclerView처럼이 :

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

좔둠이 μ‹€νŒ¨ν•˜μ§€ μ•Šλ„λ‘ μœ ν˜• 을 μ œκ³΅ν•˜λŠ” 것을 μžŠμ§€ λ§ˆμ‹­μ‹œμ˜€ ViewHolder.


λ‹΅λ³€

μœ„μ˜ λͺ¨λ“  닡변이 λ‚˜λ₯Ό μœ„ν•΄ μž‘λ™ν•˜μ§€ μ•Šμ•˜μœΌλ―€λ‘œ μš”μ²­ 된 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 개의 λ‹€λ₯Έ 버전을 λΉŒλ“œ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.


λ‹΅λ³€

λ‚˜λŠ” @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!")
                    }
                }
            }
        }
    }