Espresso,当NestedScrollView或RecyclerView在CoordinatorLayout中时,滚动不起作用
它看起来像CoordinatorLayout
打破了Espresso的行为,如scrollTo()
或RecyclerViewActions.scrollToPosition()
。
问题与NestedScrollView
对于这样的布局:
... ...
如果我尝试使用ViewActions.scrollTo()
滚动到NestedScrollView
内的任何视图,我发现第一个问题是,我得到一个PerformException
。 这是因为这个动作只支持ScrollView
而NestedScrollView
不能扩展它。 这个问题的解决方法在这里解释,基本上我们可以在scrollTo()
复制代码,并改变约束来支持NestedScrollView
。 这似乎工作,如果NestedScrollView
不是在CoordinatorLayout
但只要你把它放在一个CoordinatorLayout
滚动操作失败。
问题与RecyclerView
对于相同的布局,如果我将NestedScrollView
替换为RecyclerView
,滚动也会出现问题。
在这种情况下,我使用RecyclerViewAction.scrollToPosition(position)
。 不像NestedScrollView
,在这里我可以看到一些滚动发生。 但是,它看起来像滚动到错误的位置。 例如,如果我滚动到最后一个位置,它将显示倒数第二,但不是最后一个。 当我将RecyclerView
移出CoordinatorLayout
,滚动就像它应该的那样工作。
目前,由于这个问题,我们无法为使用CoordinatorLayout
的屏幕编写Espresso测试。 任何人遇到同样的问题或知道解决方法?
这是因为Espresso scrollTo()方法显式检查布局类,只适用于ScrollView和HorizontalScrollView。 在内部,它使用View.requestRectangleOnScreen(…),所以我期望它实际上很好的布局。
我的NestedScrollView的解决方法是采取ScrollToAction并修改该约束。 修改后的操作对于NestedScrollView可以正常工作。
更改ScrollToAction类中的方法:
public Matcher getConstraints() { return allOf(withEffectiveVisibility(Visibility.VISIBLE), isDescendantOfA(anyOf( isAssignableFrom(ScrollView.class), isAssignableFrom(HorizontalScrollView.class), isAssignableFrom(NestedScrollView.class)))); }
便利方法:
public static ViewAction betterScrollTo() { return ViewActions.actionWithAssertions(new NestedScrollToAction()); }
我有这个问题与CoordinatorLayout-> ViewPager-> NestedScrollView从我得到相同的scrollTo()行为只是在屏幕上向上轻扫工作:
onView(withId(android.R.id.content)).perform(ViewActions.swipeUp());
这个问题已经被报道(可能由OP?),参见问题203684
对这个问题的一个评论意味着当NestedScrollView位于CoordinatorLayout内部时,解决问题:
您需要移除ScrollingView或此ScrollingView所包含的任何父视图的
@string/appbar_scrolling_view_behavior
布局行为
这是一个解决方法的实现:
activity.runOnUiThread(new Runnable() { @Override public void run() { // remove CoordinatorLayout.LayoutParams from NestedScrollView NestedScrollView nestedScrollView = (NestedScrollView)activity.findViewById(scrollViewId); CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams)nestedScrollView.getLayoutParams(); params.setBehavior(null); nestedScrollView.requestLayout(); } });
我能够通过以下方式得到我的测试工作:
- 制作自定义的scrollTo()动作(由OP和Turnsole引用)
- 删除NestedScrollView的布局参数,如下所示
我做了一个NestedScrollViewScrollToAction类。
我认为这是更好的地方,而不是特定的活动。
值得一提的是,代码搜索父亲nestedScrollView,并删除它的CoordinatorLayout行为。
https://gist.github.com/miszmaniac/12f720b7e898ece55d2464fe645e1f36
Barista的scrollTo(R.id.button)
适用于各种可滚动的视图,也在NestedScrollView
。
用Espresso解决这类问题是很有用的。 我们开发和使用它只是用快速和可靠的方式编写Espresso测试。 这里有一个链接: https : //github.com/SchibstedSpain/Barista
Mido先生的解决方案可能适用于某些情况,但并不总是如此。 如果您在屏幕底部有一些视图,您的RecyclerView的滚动不会发生,因为点击将在RecyclerView之外开始。
解决此问题的一种方法是编写自定义SwipeAction。 喜欢这个:
1 – 创建CenterSwipeAction
public class CenterSwipeAction implements ViewAction { private final Swiper swiper; private final CoordinatesProvider startCoordProvide; private final CoordinatesProvider endCoordProvide; private final PrecisionDescriber precDesc; public CenterSwipeAction(Swiper swiper, CoordinatesProvider startCoordProvide, CoordinatesProvider endCoordProvide, PrecisionDescriber precDesc) { this.swiper = swiper; this.startCoordProvide = startCoordProvide; this.endCoordProvide = endCoordProvide; this.precDesc = precDesc; } @Override public Matcher getConstraints() { return withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE); } @Override public String getDescription() { return "swipe from middle of screen"; } @Override public void perform(UiController uiController, View view) { float[] startCoord = startCoordProvide.calculateCoordinates(view); float[] finalCoord = endCoordProvide.calculateCoordinates(view); float[] precision = precDesc.describePrecision(); // you could try this for several times until Swiper.Status is achieved or try count is reached try { swiper.sendSwipe(uiController, startCoord, finalCoord, precision); } catch (RuntimeException re) { throw new PerformException.Builder() .withActionDescription(this.getDescription()) .withViewDescription(HumanReadables.describe(view)) .withCause(re) .build(); } // ensures that the swipe has been run. uiController.loopMainThreadForAtLeast(ViewConfiguration.getPressedStateDuration()); } }
2 – 创建返回ViewAction的方法
private static ViewAction swipeFromCenterToTop() { return new CenterSwipeAction(Swipe.FAST, GeneralLocation.CENTER, view -> { float[] coordinates = GeneralLocation.CENTER.calculateCoordinates(view); coordinates[1] = 0; return coordinates; }, Press.FINGER); }
3 – 然后用它来滚动屏幕:
onView(withId(android.R.id.content)).perform(swipeFromCenterToTop());
而就是这样! 这样你就可以控制滚动在你的屏幕上如何发生。
下面是我做了同样的事情@米兹曼克在科特林做的。 有了Kotlin的代表团 ,它就更加清洁和简单,因为我不必重写我不需要的方法。
class ScrollToAction( private val original: android.support.test.espresso.action.ScrollToAction = android.support.test.espresso.action.ScrollToAction() ) : ViewAction by original { override fun getConstraints(): Matcher = anyOf( allOf( withEffectiveVisibility(Visibility.VISIBLE), isDescendantOfA(isAssignableFrom(NestedScrollView::class.java))), original.constraints ) }