You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Espresso嵌套RecyclerView精准滑动定位目标item并点击的实现问题

嵌套RecyclerView精准滚动定位解决方案

问题根因

你之前的方案存在两个核心问题:

  • 用全局swipeUp()单次滚动距离太大,必然容易滑过目标item,且repeatedlyUntil默认会先执行一次滑动再判断条件,即使目标已经在可视区也会触发滑动
  • nestedScrollTo()只适用于子view放在NestedScrollView下的场景,嵌套RecyclerView场景不满足约束,所以抛出异常

解决思路

不要手动控制滑动距离,直接用Espresso RecyclerView库自带的scrollTo系列API,它会自动计算滚动距离,只滚到刚好目标item可见的位置,不会滑过。因为是双层嵌套RecyclerView,需要先滚动父RecyclerView到包含目标item的子RecyclerView所在父项,再滚动子RecyclerView到目标item。

实现代码

首先确保你已经引入了和当前Espresso版本匹配的espresso-contrib依赖,否则无法使用RecyclerViewActions相关API。

1. 适配ViewHolder匹配逻辑

// 匹配包含指定文本的ViewHolder
fun withItemContainingText(targetText: String): BoundedMatcher<RecyclerView.ViewHolder, RecyclerView.ViewHolder> {
    return object : BoundedMatcher<RecyclerView.ViewHolder, RecyclerView.ViewHolder>(RecyclerView.ViewHolder::class.java) {
        override fun describeTo(description: Description) {
            description.appendText("ViewHolder containing text: $targetText")
        }

        override fun matchesSafely(holder: RecyclerView.ViewHolder): Boolean {
            val matchedViews = ArrayList<View>()
            holder.itemView.findViewsWithText(matchedViews, targetText, View.FIND_VIEWS_WITH_TEXT)
            return matchedViews.isNotEmpty()
        }
    }
}

// 匹配父RecyclerView中包含目标文本的子RecyclerView所在的父项
fun parentItemHasTargetText(targetText: String): BoundedMatcher<RecyclerView.ViewHolder, RecyclerView.ViewHolder> {
    return object : BoundedMatcher<RecyclerView.ViewHolder, RecyclerView.ViewHolder>(RecyclerView.ViewHolder::class.java) {
        override fun describeTo(description: Description) {
            description.appendText("Parent item contains child view with text: $targetText")
        }

        override fun matchesSafely(holder: RecyclerView.ViewHolder): Boolean {
            val childRecycler = holder.itemView.findViewById<RecyclerView>(R.id.recycler_list) ?: return false
            val matchedViews = ArrayList<View>()
            childRecycler.findViewsWithText(matchedViews, targetText, View.FIND_VIEWS_WITH_TEXT)
            return matchedViews.isNotEmpty()
        }
    }
}

2. 执行滚动+点击逻辑

val targetText = "你要匹配的目标文本"
// 第一步:滚动父RecyclerView到包含目标文本的父项,确保对应子RecyclerView可见
Espresso.onView(ViewMatchers.withId(R.id.parent_recycler_list))
    .perform(
        RecyclerViewActions.scrollToHolder(parentItemHasTargetText(targetText))
    )

// 第二步:滚动对应子RecyclerView到目标item,再执行点击
Espresso.onView(
    CoreMatchers.allOf(
        ViewMatchers.withId(R.id.recycler_list),
        ViewMatchers.hasDescendant(ViewMatchers.withText(targetText))
    )
).perform(
    RecyclerViewActions.actionOnHolderItem(
        withItemContainingText(targetText),
        ViewActions.click()
    )
)

特殊场景适配

如果你的子RecyclerView已经关闭了滚动功能,不需要执行第二步的子列表滚动,父列表滚动完成后直接匹配目标文本点击即可。

内容的提问来源于stack exchange,提问作者Adam Schultz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 01:12:02