CoordinatorLayout
は、scrollTo()
やRecyclerViewActions.scrollToPosition()
などのEspressoアクションの動作を中断するように見えます。
このようなレイアウトの場合:
_<Android.support.design.widget.CoordinatorLayout
Android:layout_width="match_parent"
Android:layout_height="match_parent">
<Android.support.v4.widget.NestedScrollView
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
...
</Android.support.v4.widget.NestedScrollView>
<Android.support.design.widget.AppBarLayout
Android:layout_width="match_parent"
Android:layout_height="wrap_content" >
...
</Android.support.design.widget.AppBarLayout>
</Android.support.design.widget.CoordinatorLayout>
_
ViewActions.scrollTo()
を使用してNestedScrollView
内のビューにスクロールしようとすると、最初にPerformException
が表示されるという問題があります。これは、このアクションはScrollView
のみをサポートし、NestedScrollView
はそれを拡張しないためです。この問題の回避策について説明します ここ 、基本的にはscrollTo()
にコードをコピーし、NestedScrollView
をサポートするように制約を変更できます。これは、NestedScrollView
がCoordinatorLayout
内にない場合に機能すると思われますが、CoordinatorLayout
内に配置するとすぐに、スクロールアクションが失敗します。
同じレイアウトの場合、NestedScrollView
をRecyclerView
に置き換えると、スクロールの問題も発生します。
この場合、私はRecyclerViewAction.scrollToPosition(position)
を使用しています。 NestedScrollView
とは異なり、ここではスクロールが行われているのがわかります。ただし、間違った位置にスクロールするようです。たとえば、最後の位置までスクロールすると、最後から2番目の位置が表示されますが、最後の位置は表示されません。 RecyclerView
をCoordinatorLayout
から移動すると、スクロールは正常に機能します。
現時点では、この問題のため、CoordinatorLayout
を使用する画面用のEspressoテストを作成できません。同じ問題が発生したり、回避策を知っている人はいますか?
これは、Espresso scrollTo()メソッドがレイアウトクラスを明示的にチェックし、ScrollViewおよびHorizontalScrollViewでのみ機能するために発生します。内部的にはView.requestRectangleOnScreen(...)を使用しているので、実際には多くのレイアウトで正常に機能すると予想されます。
NestedScrollViewの私の回避策は、ScrollToActionを取り、その制約を変更することでした。変更されたアクションは、その変更によりNestedScrollViewで正常に機能しました。
ScrollToActionクラスの変更されたメソッド:
public Matcher<View> 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());
}
これは、@ miszmaniacがKotlinで行ったのと同じことをした方法です。 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<View> = anyOf(
allOf(
withEffectiveVisibility(Visibility.VISIBLE),
isDescendantOfA(isAssignableFrom(NestedScrollView::class.Java))),
original.constraints
)
}
CoordinatorLayout-> ViewPager-> NestedScrollViewでこの問題が発生しました。同じscrollTo()動作を取得する簡単な回避策は、画面を上にスワイプするだけでした。
onView(withId(Android.R.id.content)).perform(ViewActions.swipeUp());
この問題は(おそらくOPによって?)報告されています。参照 問題203684
その問題へのコメントの1つは、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(R.id.button)
は、あらゆる種類のスクロール可能なビューで動作し、NestedScrollView
でも動作します。
Espressoでこの種の問題を修正すると便利です。 Espressoのテストを高速で信頼性の高い方法で作成するためだけに開発して使用しています。そしてここにリンクがあります: https://github.com/SchibstedSpain/Barista
ミド氏の解決策は、状況によってはうまくいくかもしれませんが、常にうまくいくとは限りません。画面の下部にビューがある場合、クリックがRecyclerViewの外側で開始されるため、RecyclerViewのスクロールは行われません。
この問題を回避する1つの方法は、カスタム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<View> 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());
以上です!このようにして、画面でスクロールがどのように行われるかを制御できます。
NestedScrollViewScrollToActionクラスを作成しました。
代わりにアクティビティ固有のものを作成するのに適していると思います。
言及する価値がある唯一のものは、コードが親のnestedScrollViewを検索し、そのCoordinatorLayoutの動作を削除することです。
https://Gist.github.com/miszmaniac/12f720b7e898ece55d2464fe645e1f36